diff --git a/.env.ci.test b/.env.ci similarity index 51% rename from .env.ci.test rename to .env.ci index ab52aac39..d06599331 100644 --- a/.env.ci.test +++ b/.env.ci @@ -3,6 +3,15 @@ APP_ENV=development APP_KEY= APP_DEBUG=true APP_URL=http://localhost:80 + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US +APP_MAINTENANCE_DRIVER=file +APP_MAINTENANCE_STORE=database +PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 APP_DESCRIPTION='nmrXiv is currently developed as the FAIR, consensus-driven NMR data repository and computational platform. The ultimate goal is to accelerate broader coordination and data sharing among natural product (NP) researchers by enabling the storage, management, sharing and analysis of NMR data.' COOL_OFF_PERIOD=10 SCHEMA_VERSION=beta @@ -11,19 +20,22 @@ SHOW_BANNER=true GITHUB_LICENSE_URL=https://api.github.com/licenses EUROPEMC_WS_API=https://www.ebi.ac.uk/europepmc/webservices/rest/search -ORCID_ID_SEARCH_API=https://pub.orcid.org/v2.1/search -ORCID_ID_EMPLOYMENT_API=https://pub.orcid.org/v3.0/{orcid_id}/employments -ORCID_ID_PERSON_API=https://pub.orcid.org/v3.0/{orcid_id}/person +ORCID_BASE_URL=https://pub.orcid.org/v3.0 +CM_API=https://api.naturalproducts.net/latest/ +CROSSREF_API=https://api.crossref.org/works/ +DATACITE_API=https://api.datacite.org/ +DATACITE_TEST_API=https://api.test.datacite.org LOG_CHANNEL=stack +LOG_STACK=single LOG_LEVEL=debug DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 -DB_DATABASE=nmrxiv +DB_DATABASE=nmrxiv_test DB_USERNAME=postgres -DB_PASSWORD=postgres +DB_PASSWORD=password BROADCAST_CONNECTION=log CACHE_STORE=file @@ -31,6 +43,9 @@ FILESYSTEM_DRIVER=local QUEUE_CONNECTION=redis SESSION_DRIVER=redis SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null MEMCACHED_HOST=memcached @@ -58,6 +73,7 @@ AWS_BUCKET=nmrxiv AWS_BUCKET_PUBLIC=nmrxiv-public AWS_ENDPOINT=https://s3.uni-jena.de AWS_URL=https://s3.uni-jena.de +AWS_USE_PATH_STYLE_ENDPOINT=false PUSHER_APP_ID= PUSHER_APP_KEY= @@ -67,9 +83,9 @@ PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" -SCOUT_DRIVER=meilisearch -SCOUT_PREFIX=dev_ -MEILISEARCH_HOST=https://msdev.nmrxiv.org +SCOUT_DRIVER=null +SCOUT_PREFIX=test_ +MEILISEARCH_HOST=http://localhost:7700/ MEILISEARCH_KEY= MEILISEARCH_PUBLICKEY= @@ -81,4 +97,40 @@ TWITTER_CLIENT_ID= TWITTER_CLIENT_SECRET= TWITTER_REDIRECT_URL=http://localhost:80/auth/login/twitter/callback +ORCID_CLIENT_ID= +ORCID_CLIENT_SECRET= +ORCID_REDIRECT_URL=http://localhost/auth/login/orcid/callback +ORCID_ENVIRONMENT=sandbox + +NFDIAAI_CLIENT_ID= +NFDIAAI_CLIENT_SECRET= +NFDIAAI_REDIRECT_URL="${APP_URL}/auth/login/regapp/callback" + TELESCOPE_ENABLED=false + +#DATACITE Properties +DOI_HOST=datacite +DATACITE_USERNAME= +DATACITE_SECRET= +DATACITE_PREFIX= +DATACITE_ENDPOINT=https://api.test.datacite.org + +NMRKIT_URL=https://nodejs.nmrxiv.org +PUBCHEM_URL=https://pubchem.ncbi.nlm.nih.gov +COMMON_CHEMISTRY_URL=https://commonchemistry.cas.org/api +CAS_API_TOKEN= +CHEMISTRY_STANDARDIZE_URL=https://api.naturalproducts.net/latest/chem/standardize + +BACKUP_KEEP_DAYS=7 + +# CSP Configuration +CSP_ENABLED=true +CSP_REPORT_URI="/csp-violation-report" +CSP_NONCE_ENABLED=true +CSP_ENABLED_WHILE_HOT_RELOADING=false + +# Additional CSP sources (comma-separated, no spaces) +# CSP_ADDITIONAL_CONNECT_SRC="https://api.example.com,https://analytics.example.com" +# CSP_ADDITIONAL_IMG_SRC="https://cdn.example.com,https://images.example.com" +# CSP_ADDITIONAL_SCRIPT_SRC="https://cdn.example.com" +# CSP_ADDITIONAL_STYLE_SRC="https://fonts.example.com" \ No newline at end of file diff --git a/.env.example b/.env.example index 9c88138b1..22eb52df7 100644 --- a/.env.example +++ b/.env.example @@ -1,37 +1,64 @@ +# ============================================================================ +# Application Configuration +# ============================================================================ APP_NAME=Laravel APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=localhost:80 +APP_DESCRIPTION='nmrXiv is currently developed as the FAIR, consensus-driven NMR data repository and computational platform. The ultimate goal is to accelerate broader coordination and data sharing among natural product (NP) researchers by enabling the storage, management, sharing and analysis of NMR data.' +# Localization APP_LOCALE=en APP_FALLBACK_LOCALE=en APP_FAKER_LOCALE=en_US + +# Maintenance & Workers APP_MAINTENANCE_DRIVER=file APP_MAINTENANCE_STORE=database PHP_CLI_SERVER_WORKERS=4 +# Application Specific BCRYPT_ROUNDS=12 -APP_DESCRIPTION='nmrXiv is currently developed as the FAIR, consensus-driven NMR data repository and computational platform. The ultimate goal is to accelerate broader coordination and data sharing among natural product (NP) researchers by enabling the storage, management, sharing and analysis of NMR data.' COOL_OFF_PERIOD=10 SCHEMA_VERSION=beta +BACKUP_KEEP_DAYS=7 + +MICHI_STANDARDS_URL=https://nfdi4chem.github.io/workshops/docs/michi/tabular/nmr/v1/table SHOW_BANNER=true GITHUB_LICENSE_URL=https://api.github.com/licenses EUROPEMC_WS_API=https://www.ebi.ac.uk/europepmc/webservices/rest/search -ORCID_ID_SEARCH_API=https://pub.orcid.org/v2.1/search -ORCID_ID_EMPLOYMENT_API=https://pub.orcid.org/v3.0/{orcid_id}/employments -ORCID_ID_PERSON_API=https://pub.orcid.org/v3.0/{orcid_id}/person -CM_API=https://api.cheminf.studio/latest/ -CROSSREF_API=https://api.crossref.org/works/ -DATACITE_API=https://api.datacite.org/ +ORCID_BASE_URL=https://pub.orcid.org/v3.0 +CM_API=https://api.naturalproducts.net/latest/ +CROSSREF_API=https://api.crossref.org/works/ +DATACITE_API=https://api.datacite.org/ +DATACITE_TEST_API=https://api.test.datacite.org + +# Spectra Parsing Configuration +NMRKIT_API_URL=https://nmrkit.nmrxiv.org/latest/spectra/parse/url +BIOSCHEMA_API_URL=https://nmrxiv.org/api/v1/schemas/bioschemas +SPECTRA_STORAGE_DISK=local +SPECTRA_STORAGE_PATH=spectra_parse +SPECTRA_JOB_TRIES=3 +SPECTRA_JOB_TIMEOUT=600 +SPECTRA_RETRY_COUNT=3 +SPECTRA_DOWNLOAD_TIMEOUT=300 +SPECTRA_API_TIMEOUT=300 + +# ============================================================================ +# Logging Configuration +# ============================================================================ LOG_CHANNEL=stack LOG_STACK=single LOG_LEVEL=debug +# ============================================================================ +# Database Configuration +# ============================================================================ DB_CONNECTION=pgsql DB_HOST=pgsql DB_PORT=5432 @@ -39,23 +66,35 @@ DB_DATABASE=nmrxiv DB_USERNAME=sail DB_PASSWORD=password +# ============================================================================ +# Cache, Queue, Session & Broadcasting +# ============================================================================ BROADCAST_CONNECTION=log CACHE_STORE=file -FILESYSTEM_DRIVER=local QUEUE_CONNECTION=redis + SESSION_DRIVER=redis SESSION_LIFETIME=120 SESSION_ENCRYPT=false SESSION_PATH=/ SESSION_DOMAIN=null +# ============================================================================ +# Memcached Configuration +# ============================================================================ MEMCACHED_HOST=memcached +# ============================================================================ +# Redis Configuration +# ============================================================================ REDIS_CLIENT=predis REDIS_HOST=redis REDIS_PASSWORD=null REDIS_PORT=6379 +# ============================================================================ +# Mail Configuration +# ============================================================================ MAIL_MAILER=smtp MAIL_HOST=mailpit MAIL_PORT=1025 @@ -65,18 +104,25 @@ MAIL_SCHEME=null MAIL_FROM_ADDRESS=info.nmrxiv@uni-jena.de MAIL_FROM_NAME="${APP_NAME}" +# ============================================================================ +# Filesystem Configuration +# ============================================================================ FILESYSTEM_DRIVER=minio FILESYSTEM_DRIVER_PUBLIC=minio_public +# AWS S3 Compatible Storage (MinIO) AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET=nmrxiv +AWS_BUCKET_PUBLIC=nmrxiv-public AWS_ENDPOINT=http://localhost:9000/ AWS_URL=http://localhost:9000/ AWS_USE_PATH_STYLE_ENDPOINT=false -AWS_BUCKET_PUBLIC=nmrxiv-public +# ============================================================================ +# Pusher Configuration +# ============================================================================ PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= @@ -85,42 +131,108 @@ PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" +# ============================================================================ +# Scout & Meilisearch Configuration +# ============================================================================ SCOUT_DRIVER=meilisearch SCOUT_PREFIX=dev_ MEILISEARCH_HOST=http://localhost:7700/ MEILISEARCH_KEY= MEILISEARCH_PUBLICKEY= +# ============================================================================ +# OAuth Providers +# ============================================================================ +# GitHub OAuth GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= GITHUB_REDIRECT_URL=http://localhost:80/auth/login/github/callback +# Twitter OAuth TWITTER_CLIENT_ID= TWITTER_CLIENT_SECRET= TWITTER_REDIRECT_URL=http://localhost:80/auth/login/twitter/callback +# ORCID OAuth ORCID_CLIENT_ID= ORCID_CLIENT_SECRET= ORCID_REDIRECT_URL=http://localhost/auth/login/orcid/callback ORCID_ENVIRONMENT=sandbox +ORCID_UID_FIELDNAME= +ORCID_BASE_URL=https://pub.orcid.org/v3.0 +# NFDI4Chem AAI OAuth NFDIAAI_CLIENT_ID= NFDIAAI_CLIENT_SECRET= NFDIAAI_REDIRECT_URL="${APP_URL}/auth/login/regapp/callback" -TELESCOPE_ENABLED=false - -#DATACITE Properties +# ============================================================================ +# DataCite & DOI Configuration +# ============================================================================ DOI_HOST=datacite DATACITE_USERNAME= DATACITE_SECRET= DATACITE_PREFIX= -DATACITE_ENDPOINT= +DATACITE_ENDPOINT=https://api.test.datacite.org +DATACITE_API=https://api.datacite.org/ -NMRKIT_URL=https://nodejs.nmrxiv.org -CAS_URL=https://commonchemistry.cas.org +# ============================================================================ +# External API Services +# ============================================================================ +# ROR (Research Organization Registry) +ROR_API_URL=https://api.ror.org/organizations +ROR_CLIENT_ID= + +# Europe PMC & CrossRef +EUROPEMC_WS_API=https://www.ebi.ac.uk/europepmc/webservices/rest/search +CROSSREF_API=https://api.crossref.org/works/ + +# ChemInformatics API +CM_API=https://api.cheminf.studio/latest/ + +# CAS (Chemical Abstracts Service) +CAS_API_TOKEN= + +# PubChem PUBCHEM_URL=https://pubchem.ncbi.nlm.nih.gov -COMMON_CHEMISTRY_URL=https://commonchemistry.cas.org + +# Common Chemistry +COMMON_CHEMISTRY_URL=https://commonchemistry.cas.org/api + +# Chemistry Standardization CHEMISTRY_STANDARDIZE_URL=https://api.cheminf.studio/latest/chem/standardize -BACKUP_KEEP_DAYS=7 \ No newline at end of file +# ============================================================================ +# NMRIUM Specific Services +# ============================================================================ +NMRKIT_URL=https://nodejs.nmrxiv.org +NMRIUM_URL=https://nmrium.nmrxiv.org?defaultEmptyMessage=loading&workspace=nmrXiv + +# ============================================================================ +# External Links +# ============================================================================ +MICHI_STANDARDS_URL=https://nfdi4chem.github.io/workshops/docs/michi/tabular/nmr/v1/table + +# ============================================================================ +# Content Security Policy (CSP) Configuration +# ============================================================================ +CSP_ENABLED=true +CSP_NONCE_ENABLED=false +CSP_ENABLED_WHILE_HOT_RELOADING=false + +# Additional CSP sources (comma-separated, no spaces) +# CSP_ADDITIONAL_CONNECT_SRC="https://api.example.com,https://analytics.example.com" +# CSP_ADDITIONAL_IMG_SRC="https://cdn.example.com,https://images.example.com" +# CSP_ADDITIONAL_SCRIPT_SRC="https://cdn.example.com" +# CSP_ADDITIONAL_STYLE_SRC="https://fonts.example.com" + +# ============================================================================ +# Laravel Octane Configuration +# ============================================================================ +OCTANE_SERVER=frankenphp +OCTANE_MAX_EXECUTION_TIME=3600 +OCTANE_HTTPS=true +SUPERVISOR_PHP_COMMAND="/usr/local/bin/php -d variables_order=EGPCS -d max_execution_time=600 -d max_input_time=600 -d memory_limit=4G /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port=80" + +# Optional: BrowserSync live-reload client (only when using npm/Mix/Vite with BS on this origin) +# BROWSER_SYNC_CLIENT_URL=http://localhost:3000 diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 173a22f77..a9dcad625 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -31,11 +31,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 # Not needed if lastUpdated is not enabled - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: 20 cache: npm diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index eb396fac6..9aa96e215 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -16,8 +16,9 @@ env: REPOSITORY_NAMESPACE: nfdi4chem jobs: + # Lint and Security check lint-security: - name: Lint & Security (reusable) + name: Lint & Security uses: ./.github/workflows/lint-security-check.yml permissions: contents: read @@ -27,11 +28,33 @@ jobs: run_js: true run_secrets: true + # Run tests and collect coverage + test-coverage: + name: Tests & Coverage + uses: ./.github/workflows/test-coverage.yml + needs: [lint-security] + secrets: inherit + + # Smoke test Docker images before pushing + smoke-test: + name: Smoke test Docker images + uses: ./.github/workflows/docker-smoke-test.yml + needs: [test-coverage] + # Build and publish Docker images for the development environment - setup-build-publish-deploy: - name: Build & deploy to development + build-and-push: + name: Build & push to Docker Hub runs-on: ubuntu-latest - needs: [lint-security] + needs: [test-coverage, smoke-test, lint-security] + strategy: + matrix: + image: + - name: app + file: ./deployment/Dockerfile + tag: app-dev-latest + - name: worker + file: ./deployment/Dockerfile.worker + tag: worker-dev-latest # Environment provides secrets and protection rules environment: @@ -39,7 +62,7 @@ jobs: steps: # Checkout code - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Docker Hub login (uses repository secrets) - name: Log in to Docker Hub @@ -52,28 +75,15 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - # Build + push app image (tag: app-dev-latest). Uses GHA cache for speed. - - name: Build and push App Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: ./deployment/Dockerfile - push: true - build-args: | - RELEASE_VERSION=app-dev-latest - tags: ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:app-dev-latest - cache-from: type=gha - cache-to: type=gha,mode=max - - # Build + push worker image (tag: worker-dev-latest) - - name: Build and push Worker Docker image + # Build and push image + - name: Build and push ${{ matrix.image.name }} image uses: docker/build-push-action@v6 with: context: . - file: ./deployment/Dockerfile.worker + file: ${{ matrix.image.file }} push: true build-args: | - RELEASE_VERSION=worker-dev-latest - tags: ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:worker-dev-latest - cache-from: type=gha - cache-to: type=gha,mode=max \ No newline at end of file + RELEASE_VERSION=${{ matrix.image.tag }} + tags: ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:${{ matrix.image.tag }} + cache-from: type=gha,scope=${{ matrix.image.name }} + cache-to: type=gha,mode=max,scope=${{ matrix.image.name }} \ No newline at end of file diff --git a/.github/workflows/docker-smoke-test.yml b/.github/workflows/docker-smoke-test.yml new file mode 100644 index 000000000..d68745630 --- /dev/null +++ b/.github/workflows/docker-smoke-test.yml @@ -0,0 +1,62 @@ +name: Docker Image Smoke Tests + +on: + workflow_call: + +env: + REPOSITORY_NAME: nmrxiv + REPOSITORY_NAMESPACE: nfdi4chem + +jobs: + smoke-test: + name: Smoke test containers + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + container: + - name: app + file: ./deployment/Dockerfile + wait-time: 60 + - name: worker + file: ./deployment/Dockerfile.worker + wait-time: 60 + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and export ${{ matrix.container.name }} image + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ matrix.container.file }} + load: true + tags: ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:${{ matrix.container.name }}-test + cache-from: type=gha,scope=${{ matrix.container.name }} + cache-to: type=gha,mode=max,scope=${{ matrix.container.name }} + + - name: Start ${{ matrix.container.name }} container + run: | + docker run -d --name nmrxiv-${{ matrix.container.name }}-test \ + -e APP_KEY=base64:$(openssl rand -base64 32) \ + -e APP_ENV=production \ + -e DB_CONNECTION=sqlite \ + -e CACHE_STORE=array \ + -e SESSION_DRIVER=array \ + -e QUEUE_CONNECTION=sync \ + ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:${{ matrix.container.name }}-test + + - name: Wait for ${{ matrix.container.name }} container health + uses: stringbean/docker-healthcheck-action@v3 + with: + container: nmrxiv-${{ matrix.container.name }}-test + wait-time: ${{ matrix.container.wait-time }} + require-status: running + require-healthy: true + + - name: Cleanup ${{ matrix.container.name }} container + if: always() + run: docker rm -f nmrxiv-${{ matrix.container.name }}-test diff --git a/.github/workflows/lint-security-check.yml b/.github/workflows/lint-security-check.yml index 287ab2d43..f32b14d4d 100644 --- a/.github/workflows/lint-security-check.yml +++ b/.github/workflows/lint-security-check.yml @@ -35,19 +35,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.3' + php-version: "8.4" coverage: none tools: composer extensions: mbstring, intl, pdo, pdo_mysql, pdo_pgsql ini-values: memory_limit=512M - name: Cache Composer dependencies - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | ~/.composer/cache/files @@ -74,13 +74,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: - node-version: '20' - cache: 'npm' + node-version: "20" + cache: "npm" - name: Install Node dependencies run: npm ci @@ -101,7 +101,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Gitleaks run: | @@ -136,7 +136,7 @@ jobs: - name: Upload SARIF (code scanning) if: always() - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: gitleaks.sarif - name: Fail if Gitleaks found leaks @@ -145,4 +145,4 @@ jobs: if [ "$code" -ne 0 ]; then echo "Gitleaks detected potential secrets (exit code $code). Failing job after SARIF upload." >&2 exit 1 - fi \ No newline at end of file + fi diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint-test.yml similarity index 55% rename from .github/workflows/pr-lint.yml rename to .github/workflows/pr-lint-test.yml index fb1d6f381..bb1454ffc 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint-test.yml @@ -1,4 +1,4 @@ -name: PR Lint & Security +name: PR Lint, Security and Tests on: pull_request: @@ -10,12 +10,21 @@ on: permissions: contents: read security-events: write + pull-requests: write + checks: write jobs: lint-security: name: Lint & Security (PR) + if: github.event.pull_request.merged != true uses: ./.github/workflows/lint-security-check.yml with: run_php: true run_js: true run_secrets: true + + test-coverage: + name: Tests & Coverage (PHP 8.4) + if: github.event.pull_request.merged != true + uses: ./.github/workflows/test-coverage.yml + secrets: inherit diff --git a/.github/workflows/prod-build.yml b/.github/workflows/prod-build.yml index 0cd341039..01fd5dcbd 100644 --- a/.github/workflows/prod-build.yml +++ b/.github/workflows/prod-build.yml @@ -20,22 +20,10 @@ env: REPOSITORY_NAMESPACE: nfdi4chem jobs: - lint-security: - name: Lint & Security (reusable) - uses: ./.github/workflows/lint-security-check.yml - permissions: - contents: read - security-events: write - with: - run_php: true - run_js: true - run_secrets: true - # Guard: confirm input and authorize actor guard: name: Access control and confirmation runs-on: ubuntu-latest - needs: [lint-security] steps: - name: Validate actor and confirmation shell: bash @@ -62,17 +50,49 @@ jobs: fi echo "Authorization check passed." + lint-security: + name: Lint & Security + uses: ./.github/workflows/lint-security-check.yml + needs: [guard] + permissions: + contents: read + security-events: write + with: + run_php: true + run_js: true + run_secrets: true + + # Run tests and collect coverage + test-coverage: + name: Tests & Coverage + uses: ./.github/workflows/test-coverage.yml + needs: [guard, lint-security] + secrets: inherit + + # Smoke test Docker images before pushing + smoke-test: + name: Smoke test Docker images + uses: ./.github/workflows/docker-smoke-test.yml + needs: [guard, test-coverage] + # Build and publish Docker images for the production environment - setup-build-publish-deploy-prod: - name: Deploy to prod + build-and-push: + name: Build & push to Docker Hub runs-on: ubuntu-latest - needs: [guard] + needs: [guard, test-coverage, smoke-test] + strategy: + matrix: + image: + - name: app + file: ./deployment/Dockerfile + - name: worker + file: ./deployment/Dockerfile.worker environment: name: Prod steps: # Checkout code - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Fetch latest release tag (fallback to short SHA) and short SHA for tags - name: Fetch latest release (gh) @@ -102,34 +122,18 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - # Build + push app image (tags: release, sha, latest). Uses GHA cache. - - name: Build and push App Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: ./deployment/Dockerfile - push: true - build-args: | - RELEASE_VERSION=${{ steps.fetch-latest-release.outputs.tag_name }} - tags: | - ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:app-sha-${{ steps.fetch-latest-release.outputs.sha_short }} - ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:app-${{ steps.fetch-latest-release.outputs.tag_name }} - ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:app-latest - cache-from: type=gha - cache-to: type=gha,mode=max - - # Build + push worker image (tags: release, sha, latest) - - name: Build and push Worker Docker image + # Build and push image + - name: Build and push ${{ matrix.image.name }} image uses: docker/build-push-action@v6 with: context: . - file: ./deployment/Dockerfile.worker + file: ${{ matrix.image.file }} push: true build-args: | RELEASE_VERSION=${{ steps.fetch-latest-release.outputs.tag_name }} tags: | - ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:worker-sha-${{ steps.fetch-latest-release.outputs.sha_short }} - ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:worker-${{ steps.fetch-latest-release.outputs.tag_name }} - ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:worker-latest - cache-from: type=gha - cache-to: type=gha,mode=max \ No newline at end of file + ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:${{ matrix.image.name }}-sha-${{ steps.fetch-latest-release.outputs.sha_short }} + ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:${{ matrix.image.name }}-${{ steps.fetch-latest-release.outputs.tag_name }} + ${{ env.REPOSITORY_NAMESPACE }}/${{ env.REPOSITORY_NAME }}:${{ matrix.image.name }}-latest + cache-from: type=gha,scope=${{ matrix.image.name }} + cache-to: type=gha,mode=max,scope=${{ matrix.image.name }} \ No newline at end of file diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml new file mode 100644 index 000000000..8e52091fe --- /dev/null +++ b/.github/workflows/test-coverage.yml @@ -0,0 +1,79 @@ +name: PHPUnit Tests & Coverage Analysis + +on: + workflow_call: + +jobs: + test-coverage: + name: Tests & Coverage (PHP 8.4) + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:17 + env: + POSTGRES_DB: nmrxiv_test + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + + steps: + - uses: actions/checkout@v5 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, soap, intl, gd, exif, iconv + coverage: pcov + + - name: Cache Composer packages + uses: actions/cache@v5 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install composer dependencies + run: composer install --ignore-platform-reqs + + - name: Prepare Laravel Application + run: | + php -r "file_exists('.env') || copy('.env.ci', '.env');" + + echo AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_DEV }} >> .env + echo AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_DEV }} >> .env + echo MEILISEARCH_KEY=${{ secrets.MEILISEARCH_KEY_DEV }} >> .env + echo MEILISEARCH_PUBLICKEY=${{ secrets.MEILISEARCH_PUBLICKEY_DEV }} >> .env + + php artisan key:generate + php artisan migrate --seed + + - name: Cache Node modules + uses: actions/cache@v5 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install front-end dependencies + run: npm ci + + - name: Build front-end assets + run: npm run build + + - name: Run tests and collect coverage + run: vendor/bin/phpunit --coverage-clover coverage.xml --display-skipped + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v6 + with: + files: ./coverage.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 3978544ae..b262e1c19 100644 --- a/.gitignore +++ b/.gitignore @@ -2,12 +2,14 @@ /public/hot /public/build /public/storage +/public/vendor +/public/vendor/swagger-api /storage/*.key /storage/pail +/storage/app/spectra_parse /vendor .env .env.production -.env.* .env.backup .phpunit.result.cache docker-compose.override.yml @@ -24,3 +26,8 @@ docs/.vitepress/cache **/caddy frankenphp frankenphp-worker.php + +# Coverage reports +coverage.xml +coverage-report/ +.phpunit.cache diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..2312dc587 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results deleted file mode 100644 index 4a6a82fcb..000000000 --- a/.phpunit.cache/test-results +++ /dev/null @@ -1 +0,0 @@ -{"version":1,"defects":{"Tests\\Feature\\ApiTokenPermissionsTest::test_api_token_permissions_can_be_updated":8,"Tests\\Feature\\AuthenticationTest::test_login_screen_can_be_rendered":8,"Tests\\Feature\\AuthenticationTest::test_users_can_authenticate_using_the_login_screen":8,"Tests\\Feature\\AuthenticationTest::test_users_can_not_authenticate_with_invalid_password":8,"Tests\\Feature\\BrowserSessionsTest::test_other_browser_sessions_can_be_logged_out":8,"Tests\\Feature\\CreateApiTokenTest::test_api_tokens_can_be_created":8,"Tests\\Feature\\CreateTeamTest::test_teams_can_be_created":8,"Tests\\Feature\\DeleteAccountTest::test_user_accounts_can_be_deleted":1,"Tests\\Feature\\DeleteAccountTest::test_correct_password_must_be_provided_before_account_can_be_deleted":1,"Tests\\Feature\\DeleteApiTokenTest::test_api_tokens_can_be_deleted":8,"Tests\\Feature\\DeleteTeamTest::test_teams_can_be_deleted":8,"Tests\\Feature\\DeleteTeamTest::test_personal_teams_cant_be_deleted":8,"Tests\\Feature\\EmailVerificationTest::test_email_verification_screen_can_be_rendered":8,"Tests\\Feature\\EmailVerificationTest::test_email_can_be_verified":8,"Tests\\Feature\\EmailVerificationTest::test_email_can_not_verified_with_invalid_hash":8,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":7,"Tests\\Feature\\InviteTeamMemberTest::test_team_members_can_be_invited_to_team":8,"Tests\\Feature\\InviteTeamMemberTest::test_team_member_invitations_can_be_cancelled":8,"Tests\\Feature\\LeaveTeamTest::test_users_can_leave_teams":8,"Tests\\Feature\\LeaveTeamTest::test_team_owners_cant_leave_their_own_team":8,"Tests\\Feature\\PasswordConfirmationTest::test_confirm_password_screen_can_be_rendered":8,"Tests\\Feature\\PasswordConfirmationTest::test_password_can_be_confirmed":8,"Tests\\Feature\\PasswordConfirmationTest::test_password_is_not_confirmed_with_invalid_password":8,"Tests\\Feature\\PasswordResetTest::test_reset_password_link_screen_can_be_rendered":8,"Tests\\Feature\\PasswordResetTest::test_reset_password_link_can_be_requested":8,"Tests\\Feature\\PasswordResetTest::test_reset_password_screen_can_be_rendered":8,"Tests\\Feature\\PasswordResetTest::test_password_can_be_reset_with_valid_token":8,"Tests\\Feature\\ProfileInformationTest::test_profile_information_can_be_updated":8,"Tests\\Feature\\RegistrationTest::test_registration_screen_can_be_rendered":8,"Tests\\Feature\\RegistrationTest::test_registration_screen_cannot_be_rendered_if_support_is_disabled":1,"Tests\\Feature\\RegistrationTest::test_new_users_can_register":8,"Tests\\Feature\\RemoveTeamMemberTest::test_team_members_can_be_removed_from_teams":8,"Tests\\Feature\\RemoveTeamMemberTest::test_only_team_owner_can_remove_team_members":8,"Tests\\Feature\\TwoFactorAuthenticationSettingsTest::test_two_factor_authentication_can_be_enabled":8,"Tests\\Feature\\TwoFactorAuthenticationSettingsTest::test_recovery_codes_can_be_regenerated":8,"Tests\\Feature\\TwoFactorAuthenticationSettingsTest::test_two_factor_authentication_can_be_disabled":8,"Tests\\Feature\\UpdatePasswordTest::test_password_can_be_updated":8,"Tests\\Feature\\UpdatePasswordTest::test_current_password_must_be_correct":8,"Tests\\Feature\\UpdatePasswordTest::test_new_passwords_must_match":8,"Tests\\Feature\\UpdateTeamMemberRoleTest::test_team_member_roles_can_be_updated":8,"Tests\\Feature\\UpdateTeamMemberRoleTest::test_only_team_owner_can_update_team_member_roles":8,"Tests\\Feature\\UpdateTeamNameTest::test_team_names_can_be_updated":8,"Tests\\Feature\\OEmbedTest::it_can_generate_oembed_response_for_study":8,"Tests\\Feature\\OEmbedTest::it_can_generate_oembed_response_for_dataset":8,"Tests\\Feature\\OEmbedTest::it_accepts_custom_width_and_height_parameters":8,"Tests\\Feature\\OEmbedTest::it_handles_study_without_thumbnail":8,"Tests\\Feature\\OEmbedTest::it_returns_400_when_url_parameter_is_missing":8,"Tests\\Feature\\OEmbedTest::it_returns_400_when_url_format_is_invalid":8,"Tests\\Feature\\OEmbedTest::it_returns_400_when_identifier_is_missing_from_url":8,"Tests\\Feature\\OEmbedTest::it_returns_404_when_identifier_cannot_be_resolved":8,"Tests\\Feature\\OEmbedTest::it_returns_404_when_identifier_format_is_invalid":8,"Tests\\Feature\\OEmbedTest::it_can_render_embedded_study_content":8,"Tests\\Feature\\OEmbedTest::it_can_render_embedded_dataset_content":8,"Tests\\Feature\\OEmbedTest::it_returns_400_when_embed_identifier_is_empty":8,"Tests\\Feature\\OEmbedTest::it_returns_404_when_embed_identifier_cannot_be_resolved":8,"Tests\\Feature\\OEmbedTest::it_returns_404_when_embed_identifier_format_is_invalid":8,"Tests\\Feature\\OEmbedTest::it_returns_400_for_unsupported_content_type_in_embed":8,"Tests\\Feature\\OEmbedTest::it_handles_dataset_without_associated_study":8,"Tests\\Feature\\OEmbedTest::it_handles_nmrxiv_prefix_in_identifier":8,"Tests\\Feature\\OEmbedTest::it_handles_case_insensitive_identifiers":8,"Tests\\Feature\\OEmbedTest::it_supports_json_format_parameter":8,"Tests\\Feature\\OEmbedTest::it_handles_server_errors_gracefully":8,"Tests\\Feature\\OEmbedTest::test_it_can_generate_oembed_response_for_study":7,"Tests\\Feature\\OEmbedTest::test_it_can_generate_oembed_response_for_dataset":8,"Tests\\Feature\\OEmbedTest::test_it_accepts_custom_width_and_height_parameters":8,"Tests\\Feature\\OEmbedTest::test_it_handles_study_without_thumbnail":8,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_url_parameter_is_missing":7,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_url_format_is_invalid":7,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_identifier_is_missing_from_url":8,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_identifier_cannot_be_resolved":7,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_identifier_format_is_invalid":7,"Tests\\Feature\\OEmbedTest::test_it_can_render_embedded_study_content":7,"Tests\\Feature\\OEmbedTest::test_it_can_render_embedded_dataset_content":7,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_embed_identifier_is_empty":8,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_embed_identifier_cannot_be_resolved":7,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_embed_identifier_format_is_invalid":7,"Tests\\Feature\\OEmbedTest::test_it_returns_400_for_unsupported_content_type_in_embed":7,"Tests\\Feature\\OEmbedTest::test_it_handles_dataset_without_associated_study":7,"Tests\\Feature\\OEmbedTest::test_it_handles_nmrxiv_prefix_in_identifier":8,"Tests\\Feature\\OEmbedTest::test_it_handles_case_insensitive_identifiers":8,"Tests\\Feature\\OEmbedTest::test_it_supports_json_format_parameter":8,"Tests\\Feature\\OEmbedTest::test_it_handles_server_errors_gracefully":8,"Tests\\Feature\\OEmbedTest::test_it_validates_width_and_height_parameters":7,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_string_with_json_data":7,"Tests\\Unit\\AuthorServiceTest::test_sync_authors_creates_new_authors":8,"Tests\\Unit\\AuthorServiceTest::test_sync_authors_reuses_existing_authors_by_email":8,"Tests\\Unit\\AuthorServiceTest::test_sync_authors_handles_empty_array":8,"Tests\\Unit\\AuthorServiceTest::test_sync_authors_validates_required_fields":8,"Tests\\Unit\\AuthorServiceTest::test_remove_author_from_project_detaches_successfully":8,"Tests\\Unit\\AuthorServiceTest::test_remove_author_from_project_handles_nonexistent_author":8,"Tests\\Unit\\AuthorServiceTest::test_update_contributor_type_changes_role":8,"Tests\\Unit\\AuthorServiceTest::test_update_contributor_type_validates_role":8,"Tests\\Unit\\AuthorServiceTest::test_sync_authors_uses_database_transaction":8,"Tests\\Unit\\AuthorServiceTest::test_sync_authors_creates_composite_index_optimized_queries":8,"Tests\\Unit\\AuthorServiceTest::test_sync_authors_prevents_n_plus_one_queries":8,"Tests\\Unit\\ProcessDraftELNSubmissionProxyTest::test_http_client_uses_proxy_when_configured":8,"Tests\\Unit\\ProcessDraftELNSubmissionProxyTest::test_http_client_works_without_proxy_configuration":8,"Tests\\Feature\\ManageAuthorsTest::test_author_cannot_be_updated_or_detached_if_project_is_public":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_can_be_created_with_submitted_through_field":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_factory_creates_sample_with_null_submitted_through_by_default":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_factory_can_create_sample_with_eln_state":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_factory_can_create_sample_with_custom_submitted_through":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_can_be_updated_with_submitted_through_field":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_submitted_through_field_is_fillable":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_submitted_through_field_accepts_null_values":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_submitted_through_field_accepts_various_string_values":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_can_store_processing_logs_from_draft":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_can_be_created_with_submitted_through_field":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_creates_study_with_null_submitted_through_by_default":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_can_create_study_with_eln_state":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_can_create_study_with_default_eln":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_can_create_study_with_custom_submitted_through":8,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_can_be_updated_with_submitted_through_field":8,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_create_tracking_success":8,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_create_tracking_authentication_failure":5,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_trackings_success":8,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_tracking_by_id_success":8,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_tracking_items_success":8,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_tracking_item_by_name_success":8,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_create_eln_submission_tracking_success":7,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_update_eln_submission_status_success":8,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_api_error_handling":5,"Tests\\Feature\\ELNSubmissionTrackingTest::test_eln_submission_creates_tracking_when_received":7,"Tests\\Feature\\ELNSubmissionTrackingTest::test_study_publication_creates_tracking_when_published":8,"Tests\\Feature\\ELNSubmissionTrackingTest::test_tracking_failure_does_not_break_submission":7,"Tests\\Feature\\ELNSubmissionTrackingTest::test_non_eln_study_publication_does_not_create_tracking":8,"Tests\\Feature\\SearchControllerSecurityTest::test_sql_injection_in_query_parameter":8,"Tests\\Feature\\SearchControllerSecurityTest::test_sql_injection_in_filter_queries":7,"Tests\\Feature\\SearchControllerSecurityTest::test_input_validation":8,"Tests\\Feature\\SearchControllerSecurityTest::test_query_length_limits":8,"Tests\\Feature\\SearchControllerSecurityTest::test_control_character_filtering":8,"Tests\\Feature\\SearchControllerSecurityTest::test_smiles_injection_attempts":7,"Tests\\Feature\\SearchControllerSecurityTest::test_inchi_injection_attempts":8,"Tests\\Feature\\SearchControllerSecurityTest::test_legitimate_queries_still_work":8,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_handles_ranges":5,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_sanitizes_text":5,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_validates_booleans":5,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_validates_database_names":5,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_malicious_filter_queries_handled_safely":5,"Tests\\Unit\\MarkdownSecurityTest::test_md_function_allows_safe_html":7,"Tests\\Unit\\MarkdownSecurityTest::test_md_function_preserves_markdown_formatting":7,"Tests\\Unit\\MarkdownXSSSecurityTest::test_javascript_urls_are_removed":7,"Tests\\Feature\\RouteStructureTest::test_project_route_accepts_valid_identifiers":8,"Tests\\Feature\\RouteStructureTest::test_sample_route_accepts_valid_identifiers":8,"Tests\\Feature\\RouteStructureTest::test_compound_route_accepts_valid_identifiers":8},"times":{"Tests\\Unit\\ExampleTest::test_example":0.003,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.181,"Tests\\Feature\\ApiTokenPermissionsTest::test_api_token_permissions_can_be_updated":0.047,"Tests\\Feature\\AuthenticationTest::test_login_screen_can_be_rendered":0.035,"Tests\\Feature\\AuthenticationTest::test_users_can_authenticate_using_the_login_screen":0.061,"Tests\\Feature\\AuthenticationTest::test_users_can_not_authenticate_with_invalid_password":0.052,"Tests\\Feature\\BrowserSessionsTest::test_other_browser_sessions_can_be_logged_out":0.099,"Tests\\Feature\\CreateApiTokenTest::test_api_tokens_can_be_created":0.01,"Tests\\Feature\\CreateTeamTest::test_teams_can_be_created":0.011,"Tests\\Feature\\DeleteAccountTest::test_user_accounts_can_be_deleted":0.013,"Tests\\Feature\\DeleteAccountTest::test_correct_password_must_be_provided_before_account_can_be_deleted":0,"Tests\\Feature\\DeleteApiTokenTest::test_api_tokens_can_be_deleted":0.007,"Tests\\Feature\\DeleteTeamTest::test_teams_can_be_deleted":0.071,"Tests\\Feature\\DeleteTeamTest::test_personal_teams_cant_be_deleted":0.211,"Tests\\Feature\\EmailVerificationTest::test_email_verification_screen_can_be_rendered":0.015,"Tests\\Feature\\EmailVerificationTest::test_email_can_be_verified":0.007,"Tests\\Feature\\EmailVerificationTest::test_email_can_not_verified_with_invalid_hash":0.007,"Tests\\Feature\\InviteTeamMemberTest::test_team_members_can_be_invited_to_team":0.019,"Tests\\Feature\\InviteTeamMemberTest::test_team_member_invitations_can_be_cancelled":0.007,"Tests\\Feature\\LeaveTeamTest::test_users_can_leave_teams":0.01,"Tests\\Feature\\LeaveTeamTest::test_team_owners_cant_leave_their_own_team":0.006,"Tests\\Feature\\PasswordConfirmationTest::test_confirm_password_screen_can_be_rendered":0.012,"Tests\\Feature\\PasswordConfirmationTest::test_password_can_be_confirmed":0.047,"Tests\\Feature\\PasswordConfirmationTest::test_password_is_not_confirmed_with_invalid_password":0.209,"Tests\\Feature\\PasswordResetTest::test_reset_password_link_screen_can_be_rendered":0.007,"Tests\\Feature\\PasswordResetTest::test_reset_password_link_can_be_requested":0.216,"Tests\\Feature\\PasswordResetTest::test_reset_password_screen_can_be_rendered":0.216,"Tests\\Feature\\PasswordResetTest::test_password_can_be_reset_with_valid_token":0.229,"Tests\\Feature\\ProfileInformationTest::test_profile_information_can_be_updated":0.078,"Tests\\Feature\\RegistrationTest::test_registration_screen_can_be_rendered":0.006,"Tests\\Feature\\RegistrationTest::test_registration_screen_cannot_be_rendered_if_support_is_disabled":0,"Tests\\Feature\\RegistrationTest::test_new_users_can_register":0.012,"Tests\\Feature\\RemoveTeamMemberTest::test_team_members_can_be_removed_from_teams":0.008,"Tests\\Feature\\RemoveTeamMemberTest::test_only_team_owner_can_remove_team_members":0.009,"Tests\\Feature\\TwoFactorAuthenticationSettingsTest::test_two_factor_authentication_can_be_enabled":0.009,"Tests\\Feature\\TwoFactorAuthenticationSettingsTest::test_recovery_codes_can_be_regenerated":0.009,"Tests\\Feature\\TwoFactorAuthenticationSettingsTest::test_two_factor_authentication_can_be_disabled":0.006,"Tests\\Feature\\UpdatePasswordTest::test_password_can_be_updated":0.048,"Tests\\Feature\\UpdatePasswordTest::test_current_password_must_be_correct":0.086,"Tests\\Feature\\UpdatePasswordTest::test_new_passwords_must_match":0.086,"Tests\\Feature\\UpdateTeamMemberRoleTest::test_team_member_roles_can_be_updated":0.01,"Tests\\Feature\\UpdateTeamMemberRoleTest::test_only_team_owner_can_update_team_member_roles":0.746,"Tests\\Feature\\UpdateTeamNameTest::test_team_names_can_be_updated":0.008,"Tests\\Feature\\OEmbedTest::test_it_can_generate_oembed_response_for_study":0.009,"Tests\\Feature\\OEmbedTest::test_it_can_generate_oembed_response_for_dataset":0.005,"Tests\\Feature\\OEmbedTest::test_it_accepts_custom_width_and_height_parameters":0.005,"Tests\\Feature\\OEmbedTest::test_it_handles_study_without_thumbnail":0.007,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_url_parameter_is_missing":0.005,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_url_format_is_invalid":0.004,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_identifier_is_missing_from_url":0.004,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_identifier_cannot_be_resolved":0.007,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_identifier_format_is_invalid":0.009,"Tests\\Feature\\OEmbedTest::test_it_can_render_embedded_study_content":0.017,"Tests\\Feature\\OEmbedTest::test_it_can_render_embedded_dataset_content":0.013,"Tests\\Feature\\OEmbedTest::test_it_returns_400_when_embed_identifier_is_empty":0.005,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_embed_identifier_cannot_be_resolved":0.004,"Tests\\Feature\\OEmbedTest::test_it_returns_404_when_embed_identifier_format_is_invalid":0.004,"Tests\\Feature\\OEmbedTest::test_it_returns_400_for_unsupported_content_type_in_embed":0.012,"Tests\\Feature\\OEmbedTest::test_it_handles_dataset_without_associated_study":0.006,"Tests\\Feature\\OEmbedTest::test_it_handles_nmrxiv_prefix_in_identifier":0.006,"Tests\\Feature\\OEmbedTest::test_it_handles_case_insensitive_identifiers":0.008,"Tests\\Feature\\OEmbedTest::test_it_supports_json_format_parameter":0.006,"Tests\\Feature\\OEmbedTest::test_it_handles_server_errors_gracefully":0.006,"Tests\\Feature\\OEmbedTest::test_it_returns_500_when_identifier_format_is_invalid":0.004,"Tests\\Feature\\OEmbedTest::test_it_returns_500_when_embed_identifier_format_is_invalid":0.004,"Tests\\Feature\\OEmbedTest::test_it_returns_404_for_unsupported_content_type_in_embed":0.005,"Tests\\Feature\\OEmbedTest::test_it_blocks_private_content_in_oembed":0.007,"Tests\\Feature\\OEmbedTest::test_it_blocks_private_content_in_embed":0.006,"Tests\\Feature\\OEmbedTest::test_it_blocks_external_domain_urls":0.007,"Tests\\Feature\\OEmbedTest::test_it_validates_width_and_height_parameters":0.009,"Tests\\Feature\\OEmbedTest::test_iframe_html_is_properly_sanitized":0.005,"Tests\\Feature\\ManageAuthorsTest::test_author_can_be_created_and_updated":0.025,"Tests\\Feature\\ManageAuthorsTest::test_author_can_be_updated":0.024,"Tests\\Feature\\ManageAuthorsTest::test_author_can_be_detached":0.019,"Tests\\Feature\\ManageAuthorsTest::test_author_cannot_be_updated_or_deleted_by_reviewer":0.016,"Tests\\Feature\\ManageAuthorsTest::test_author_cannot_be_updated_or_detached_if_project_is_public":0.013,"Tests\\Feature\\ManageAuthorsTest::test_role_of_an_author_can_be_updated":0.013,"Tests\\Feature\\ManageAuthorsTest::test_role_of_an_author_cannot_be_updated_by_reviewer":0.011,"Tests\\Feature\\ManageAuthorsTest::test_role_of_an_author_cannot_be_updated_for_random_contributor_types":0.011,"Tests\\Unit\\ProcessDraftELNSubmissionProxyTest::test_http_client_uses_proxy_when_configured":0.067,"Tests\\Unit\\ProcessDraftELNSubmissionProxyTest::test_http_client_works_without_proxy_configuration":0.003,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_string_preserves_newlines":0.003,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_string_removes_problematic_unicode":0,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_string_preserves_ascii_printable":0,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_string_with_json_data":0,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_in_array_preserves_newlines":0,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_in_nmrium_data_preserves_newlines":0,"Tests\\Unit\\HelperFunctionsTest::test_sanitize_unicode_string_converts_escape_sequences":0,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_can_be_created_with_submitted_through_field":0.007,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_factory_creates_sample_with_null_submitted_through_by_default":0.002,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_factory_can_create_sample_with_eln_state":0.002,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_factory_can_create_sample_with_custom_submitted_through":0.002,"Tests\\Feature\\SampleSubmittedThroughTest::test_sample_can_be_updated_with_submitted_through_field":0.002,"Tests\\Feature\\SampleSubmittedThroughTest::test_submitted_through_field_is_fillable":0.001,"Tests\\Feature\\SampleSubmittedThroughTest::test_submitted_through_field_accepts_null_values":0.003,"Tests\\Feature\\SampleSubmittedThroughTest::test_submitted_through_field_accepts_various_string_values":0.01,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_can_be_created_with_submitted_through_field":0.008,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_creates_study_with_null_submitted_through_by_default":0.004,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_can_create_study_with_eln_state":0.003,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_can_create_study_with_default_eln":0.003,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_factory_can_create_study_with_custom_submitted_through":0.003,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_can_be_updated_with_submitted_through_field":0.005,"Tests\\Feature\\SampleSubmittedThroughTest::test_study_can_store_processing_logs_from_draft":0.003,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_create_tracking_success":0.02,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_create_tracking_authentication_failure":0.004,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_trackings_success":0.001,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_tracking_by_id_success":0.001,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_tracking_items_success":0.001,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_get_tracking_item_by_name_success":0.001,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_create_eln_submission_tracking_success":0.001,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_update_eln_submission_status_success":0.001,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_api_error_handling":0.001,"Tests\\Feature\\ELNSubmissionTrackingTest::test_eln_submission_creates_tracking_when_received":0.356,"Tests\\Feature\\ELNSubmissionTrackingTest::test_study_publication_creates_tracking_when_published":0.025,"Tests\\Feature\\ELNSubmissionTrackingTest::test_tracking_failure_does_not_break_submission":0.015,"Tests\\Feature\\ELNSubmissionTrackingTest::test_non_eln_study_publication_does_not_create_tracking":0.007,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_is_enabled_returns_correct_value":0,"Tests\\Unit\\ChemotionRepositoryTrackerServiceTest::test_status_validation":0.001,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_sanitize_query_removes_control_characters":0.003,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_sanitize_query_trims_whitespace":0,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_sanitize_query_limits_length":0,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_sanitize_query_handles_null":0,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_validates_fields":0,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_handles_ranges":0.005,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_sanitizes_text":0,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_validates_booleans":0,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_build_secure_filter_query_validates_database_names":0,"Tests\\Unit\\SearchControllerSecurityUnitTest::test_malicious_filter_queries_handled_safely":0,"Tests\\Feature\\SearchControllerSecurityTest::test_sql_injection_in_query_parameter":0.037,"Tests\\Feature\\SearchControllerSecurityTest::test_sql_injection_in_filter_queries":0.012,"Tests\\Feature\\SearchControllerSecurityTest::test_input_validation":0.008,"Tests\\Feature\\SearchControllerSecurityTest::test_query_length_limits":0.003,"Tests\\Feature\\SearchControllerSecurityTest::test_control_character_filtering":0.003,"Tests\\Feature\\SearchControllerSecurityTest::test_smiles_injection_attempts":0.006,"Tests\\Feature\\SearchControllerSecurityTest::test_inchi_injection_attempts":0.005,"Tests\\Feature\\SearchControllerSecurityTest::test_legitimate_queries_still_work":0.006,"Tests\\Unit\\MarkdownSecurityTest::test_md_function_sanitizes_script_tags":0.004,"Tests\\Unit\\MarkdownSecurityTest::test_md_function_allows_safe_html":0.008,"Tests\\Unit\\MarkdownSecurityTest::test_md_function_handles_empty_input":0,"Tests\\Unit\\MarkdownSecurityTest::test_md_function_prevents_various_xss_attacks":0,"Tests\\Unit\\MarkdownSecurityTest::test_md_function_preserves_markdown_formatting":0,"Tests\\Unit\\MarkdownXSSSecurityTest::test_script_tags_are_removed":0.003,"Tests\\Unit\\MarkdownXSSSecurityTest::test_event_handlers_are_removed":0,"Tests\\Unit\\MarkdownXSSSecurityTest::test_javascript_urls_are_removed":0,"Tests\\Unit\\MarkdownXSSSecurityTest::test_dangerous_html_tags_are_removed":0,"Tests\\Unit\\MarkdownXSSSecurityTest::test_safe_markdown_is_preserved":0,"Tests\\Unit\\MarkdownXSSSecurityTest::test_empty_input_is_handled_safely":0,"Tests\\Unit\\MarkdownXSSSecurityTest::test_sanitize_html_function_prevents_xss":0,"Tests\\Unit\\MarkdownXSSSecurityTest::test_sanitize_html_handles_license_content":0,"Tests\\Feature\\RouteStructureTest::test_new_project_route_structure_is_registered":0.003,"Tests\\Feature\\RouteStructureTest::test_new_sample_route_structure_is_registered":0,"Tests\\Feature\\RouteStructureTest::test_new_compound_route_structure_is_registered":0,"Tests\\Feature\\RouteStructureTest::test_project_route_accepts_valid_identifiers":0.001,"Tests\\Feature\\RouteStructureTest::test_sample_route_accepts_valid_identifiers":0,"Tests\\Feature\\RouteStructureTest::test_compound_route_accepts_valid_identifiers":0}} \ No newline at end of file diff --git a/README.md b/README.md index 56486cb62..4d9eb7041 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-blue.svg)](https://GitHub.com/NFDI4Chem/nmrxiv/graphs/commit-activity) [![GitHub issues](https://img.shields.io/github/issues/NFDI4Chem/nmrxiv.svg)](https://GitHub.com/NFDI4Chem/nmrxiv/issues/) [![GitHub contributors](https://img.shields.io/github/contributors/NFDI4Chem/nmrxiv.svg)](https://GitHub.com/NFDI4Chem/nmrxiv/graphs/contributors/) -![Workflow](https://github.com/NFDI4Chem/nmrxiv/actions/workflows/build.yml/badge.svg) +![Workflow](https://github.com/NFDI4Chem/nmrxiv/actions/workflows/prod-build.yml/badge.svg) ![Workflow](https://github.com/NFDI4Chem/nmrxiv/actions/workflows/release-please.yml/badge.svg) [![Powered by Laravel](https://img.shields.io/badge/Powered%20by-Laravel-red.svg?style=flat&logo=Laravel)](https://laravel.com) diff --git a/app/Actions/Author/SyncProjectAuthors.php b/app/Actions/Author/SyncProjectAuthors.php index c24bb0537..23b4c395d 100644 --- a/app/Actions/Author/SyncProjectAuthors.php +++ b/app/Actions/Author/SyncProjectAuthors.php @@ -7,6 +7,7 @@ use App\Models\Project; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\ValidationException; class SyncProjectAuthors { @@ -18,7 +19,7 @@ public function __construct(private UpdateProject $updater) {} * @param array> $authors * @return array * - * @throws \Illuminate\Validation\ValidationException + * @throws ValidationException */ public function handle(Project $project, array $authors): array { @@ -64,6 +65,7 @@ private function validateAuthorData(array $authorData): void 'email_id' => ['nullable', 'email', 'max:320'], 'orcid_id' => ['nullable', 'string', 'max:19'], 'affiliation' => ['nullable', 'string', 'max:500'], + 'ror_id' => ['nullable', 'string', 'max:255'], 'contributor_type' => ['nullable', 'string', 'max:50'], ])->validate(); } @@ -105,6 +107,7 @@ private function prepareAuthorAttributes(array $authorData, string $familyName, 'orcid_id' => $authorData['orcid_id'] ?? null, 'email_id' => $authorData['email_id'] ?? null, 'affiliation' => $authorData['affiliation'] ?? null, + 'ror_id' => $authorData['ror_id'] ?? null, ]; } } diff --git a/app/Actions/Citation/RemoveCitation.php b/app/Actions/Citation/RemoveCitation.php index d008171e6..adaf9a668 100644 --- a/app/Actions/Citation/RemoveCitation.php +++ b/app/Actions/Citation/RemoveCitation.php @@ -2,18 +2,18 @@ namespace App\Actions\Citation; -use App\Actions\Project\UpdateProject; use App\Models\Project; +use App\Models\Study; use Illuminate\Support\Facades\DB; class RemoveCitation { - public function __construct(private UpdateProject $updater) {} + public function __construct(private SyncCitationPivot $citationPivot) {} - public function remove(Project $project, int $citationId): void + public function remove(Project|Study $owner, int $citationId): void { - DB::transaction(function () use ($project, $citationId): void { - $this->updater->detachCitation($project, $citationId); + DB::transaction(function () use ($owner, $citationId): void { + $this->citationPivot->detach($owner, $citationId); }); } } diff --git a/app/Actions/Citation/SyncCitationPivot.php b/app/Actions/Citation/SyncCitationPivot.php new file mode 100644 index 000000000..cad633dd1 --- /dev/null +++ b/app/Actions/Citation/SyncCitationPivot.php @@ -0,0 +1,66 @@ + $citations + */ + public function sync(Project|Study $owner, iterable $citations, User $user): void + { + $citations_map = []; + foreach ($citations as $citation) { + $citations_map[$citation->id] = ['user' => (string) $user->id]; + } + + $this->citationsRelation($owner)->sync($citations_map); + } + + public function detach(Project|Study $owner, int $citationId): void + { + $this->citationsRelation($owner)->detach($citationId); + } + + /** + * Union of existing study citations and project citations; pivot `user` prefers the study row, then the project row. + * + * @param Collection $projectCitations + */ + public function mergeProjectCitationsOntoStudy(Study $study, Collection $projectCitations): void + { + if ($projectCitations->isEmpty()) { + return; + } + + $study->load('linkedCitations'); + + $byIdOnStudy = $study->linkedCitations->keyBy('id'); + $byIdOnProject = $projectCitations->keyBy('id'); + + $allIds = $byIdOnStudy->keys()->merge($byIdOnProject->keys())->unique()->values(); + + $citations_map = []; + foreach ($allIds as $citationId) { + $pivotUser = $byIdOnStudy->get($citationId)?->pivot?->user + ?? $byIdOnProject->get($citationId)?->pivot?->user; + $citations_map[$citationId] = ['user' => $pivotUser !== null ? (string) $pivotUser : null]; + } + + $study->linkedCitations()->sync($citations_map); + } + + public function citationsRelation(Project|Study $owner): BelongsToMany + { + return $owner instanceof Study + ? $owner->linkedCitations() + : $owner->citations(); + } +} diff --git a/app/Actions/Citation/SyncCitations.php b/app/Actions/Citation/SyncCitations.php index c846d96fc..f4a310c82 100644 --- a/app/Actions/Citation/SyncCitations.php +++ b/app/Actions/Citation/SyncCitations.php @@ -2,81 +2,172 @@ namespace App\Actions\Citation; -use App\Actions\Project\UpdateProject; use App\Models\Citation; use App\Models\Project; +use App\Models\Study; use App\Models\User; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; +use Illuminate\Validation\ValidationException; class SyncCitations { - public function __construct(private UpdateProject $updater) {} + public function __construct(private SyncCitationPivot $citationPivot) {} /** - * Process and sync citations for a project. + * Process and sync citations for a project or study. * * @param array> $citations * @return array * - * @throws \Illuminate\Validation\ValidationException + * @throws ValidationException */ - public function sync(Project $project, array $citations, User $user): array + public function sync(Project|Study $owner, array $citations, User $user): array { if (empty($citations)) { return []; } - // Eager load existing citations to prevent N+1 queries - $project->load('citations'); + $this->loadCitationRelation($owner); $processedCitations = []; foreach ($citations as $citationData) { $this->validateCitationData($citationData); - $doi = $citationData['doi']; - - if (! is_null($doi)) { - $citation = $this->findOrCreateCitation($project, $citationData, $doi); - $processedCitations[] = $citation; - } + $citation = $this->findOrCreateCitation($owner, $citationData); + $this->rememberCitation($owner, $citation); + $processedCitations[] = $citation; } - DB::transaction(function () use ($project, $processedCitations, $user): void { - $this->updater->syncCitations($project, $processedCitations, $user); + DB::transaction(function () use ($owner, $processedCitations, $user): void { + $this->citationPivot->sync($owner, $processedCitations, $user); }); return $processedCitations; } + /** + * Sync ELN-style citation rows onto a study (JSON column is updated separately by the caller). + * + * @param array> $rawCitations + */ + public function syncFromStudyElnPayload(Study $study, array $rawCitations, User $user): void + { + if ($rawCitations === []) { + return; + } + + $rows = array_is_list($rawCitations) ? $rawCitations : [$rawCitations]; + $mapped = []; + foreach ($rows as $item) { + if (! is_array($item)) { + continue; + } + $mapped[] = $this->mapElnCitationRowToValidatedPayload($item); + } + + if ($mapped === []) { + return; + } + + $this->sync($study, $mapped, $user); + } + + /** + * @param array $item + * @return array + */ + private function mapElnCitationRowToValidatedPayload(array $item): array + { + $title = trim((string) ($item['title'] ?? $item['name'] ?? '')); + if ($title === '') { + $title = 'Untitled'; + } + + $authors = trim((string) ($item['authors'] ?? $item['author'] ?? '')); + if ($authors === '') { + $authors = 'Unknown'; + } + + $doi = $item['doi'] ?? null; + if (! is_string($doi) || trim($doi) === '') { + $doi = $this->extractDoiFromUrl($item['url'] ?? null); + } elseif (is_string($doi)) { + $doi = trim($doi) !== '' ? trim($doi) : null; + } + + $citationText = $item['citation_text'] ?? null; + + return [ + 'title' => $title, + 'authors' => $authors, + 'doi' => $doi, + 'citation_text' => is_string($citationText) ? trim($citationText) : null, + ]; + } + + private function extractDoiFromUrl(mixed $url): ?string + { + if (! is_string($url) || trim($url) === '') { + return null; + } + + if (preg_match('~doi\.org/([^?\s#]+)~i', $url, $matches)) { + return rawurldecode($matches[1]); + } + + return null; + } + /** * Validate citation data against validation rules. * * @param array $citationData * - * @throws \Illuminate\Validation\ValidationException + * @throws ValidationException */ private function validateCitationData(array $citationData): void { Validator::make($citationData, [ 'title' => ['required', 'string'], - 'doi' => ['required', 'string'], + 'doi' => ['nullable', 'string'], 'authors' => ['required', 'string'], 'citation_text' => ['nullable', 'string'], ])->validate(); } /** - * Find existing citation for project or create new one. + * Find existing citation for owner or create new one. * * @param array $citationData */ - private function findOrCreateCitation(Project $project, array $citationData, string $doi): Citation + private function findOrCreateCitation(Project|Study $owner, array $citationData): Citation { - $existingCitation = $project->citations->filter(function ($citation) use ($doi) { - return $doi === $citation->doi; - })->first(); + $doi = $this->normalizeDoi($citationData['doi'] ?? null); + $title = $this->normalizeText($citationData['title'] ?? null); + $titleSlug = $this->normalizeTitleSlug($citationData['title'] ?? null); + + $existingCitation = null; + + if (! empty($citationData['id'])) { + $existingCitation = $this->findCitationByIdForOwner($owner, (int) $citationData['id']); + } + + if (! $existingCitation && ! is_null($doi)) { + $existingCitation = $this->citationCollection($owner)->firstWhere('doi', $doi); + } + + if (! $existingCitation && ! is_null($titleSlug)) { + $existingCitation = $this->citationCollection($owner)->first(function ($citation) use ($titleSlug): bool { + $citationSlug = $this->normalizeTitleSlug($citation->title_slug ?? $citation->title); + + return $citationSlug === $titleSlug; + }); + } if ($existingCitation) { $existingCitation->update($this->prepareCitationAttributes($citationData)); @@ -96,10 +187,104 @@ private function findOrCreateCitation(Project $project, array $citationData, str private function prepareCitationAttributes(array $citationData): array { return [ - 'doi' => $citationData['doi'] ?? null, - 'title' => $citationData['title'] ?? null, - 'authors' => $citationData['authors'] ?? null, - 'citation_text' => $citationData['citation_text'] ?? null, + 'doi' => $this->normalizeDoi($citationData['doi'] ?? null), + 'title' => $this->normalizeText($citationData['title'] ?? null), + 'title_slug' => $this->normalizeTitleSlug($citationData['title'] ?? null), + 'authors' => $this->normalizeText($citationData['authors'] ?? null), + 'citation_text' => $this->normalizeText($citationData['citation_text'] ?? null), ]; } + + private function rememberCitation(Project|Study $owner, Citation $citation): void + { + $citations = $this->citationCollection($owner); + + if (! $citations->contains('id', $citation->id)) { + $relationName = $owner instanceof Study ? 'linkedCitations' : 'citations'; + $owner->setRelation($relationName, $citations->push($citation)); + } + } + + private function findCitationByIdForOwner(Project|Study $owner, int $id): ?Citation + { + return $this->citationCollection($owner)->firstWhere('id', $id) + ?? $this->citationRelationQuery($owner)->whereKey($id)->first() + ?? Citation::query()->find($id); + } + + /** + * @return BelongsToMany|BelongsToMany + */ + private function citationRelationQuery(Project|Study $owner) + { + return $owner instanceof Study + ? $owner->linkedCitations() + : $owner->citations(); + } + + private function loadCitationRelation(Project|Study $owner): void + { + if ($owner instanceof Study) { + $owner->load('linkedCitations'); + } else { + $owner->load('citations'); + } + } + + /** + * @return Collection + */ + private function citationCollection(Project|Study $owner): Collection + { + return $owner instanceof Study + ? $owner->linkedCitations + : $owner->citations; + } + + private function normalizeTitleSlug(mixed $title): ?string + { + $normalizedTitle = $this->normalizeText($title); + + if (is_null($normalizedTitle)) { + return null; + } + + $slug = Str::slug($normalizedTitle); + + if ($slug === '') { + return null; + } + + return $slug; + } + + private function normalizeDoi(mixed $doi): ?string + { + if (! is_string($doi)) { + return null; + } + + $normalizedDoi = trim($doi); + + if ($normalizedDoi === '') { + return null; + } + + return $normalizedDoi; + } + + private function normalizeText(mixed $value): ?string + { + if (! is_string($value)) { + return null; + } + + $normalizedValue = trim($value); + + if ($normalizedValue === '') { + return null; + } + + return $normalizedValue; + } } diff --git a/app/Actions/Draft/CreateDraft.php b/app/Actions/Draft/CreateDraft.php index c026b0aaa..69ed1629e 100644 --- a/app/Actions/Draft/CreateDraft.php +++ b/app/Actions/Draft/CreateDraft.php @@ -16,7 +16,7 @@ public function execute(User $user, array $options = []): Draft [$user_id, $team_id] = $user->getUserTeamData(); $id = Str::uuid(); - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $path = $this->generateDraftPath($environment, $user_id, $id); $name = $this->generateDraftName($id, $options); diff --git a/app/Actions/Draft/ProcessDraft.php b/app/Actions/Draft/ProcessDraft.php index 5cfb2737d..beb1e6016 100644 --- a/app/Actions/Draft/ProcessDraft.php +++ b/app/Actions/Draft/ProcessDraft.php @@ -167,6 +167,8 @@ public function updateExistingProject(Project $project, Draft $draft): void */ public function cleanupOrphanedData(Project $project): void { + $project->loadMissing(['studies.fsObject', 'studies.datasets.fsObject']); + foreach ($project->studies as $study) { $fsObject = $study->fsObject; if (! $fsObject || $fsObject->status == 'missing') { @@ -364,7 +366,7 @@ public function finalizeProcessing(Draft $draft, Project $project): Response|Jso { $draft->save(); - $studies = json_decode($project->studies()->orderBy('name')->get()->load(['datasets', 'sample.molecules', 'tags'])); + $studies = $project->studies()->orderBy('name')->get()->load(['datasets', 'sample.molecules', 'tags']); if (count($studies) == 0) { return redirect()->back()->withErrors(['studies' => 'nmrXiv requires raw or processed raw instrument output files. If you data is from a single sample organise all the files in one folder and click proceed. If you have multiple samples, group your data in subfolders with each subfolder corresponding to a sample. Thank you.']); @@ -373,6 +375,12 @@ public function finalizeProcessing(Draft $draft, Project $project): Response|Jso Log::info('Finalizing processing for draft '.$draft->id); Log::info('Studies count: '.count($studies)); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_draft_finalize_dispatch_archive_study', + 'project_id' => $project->id, + 'draft_id' => $draft->id, + ]); + ArchiveStudy::dispatch($project); // log archiving study dispatch @@ -381,6 +389,62 @@ public function finalizeProcessing(Draft $draft, Project $project): Response|Jso return response()->json([ 'project' => $project->load(['owner', 'citations', 'authors']), 'studies' => $studies, + 'warnings' => $this->detectNestedStudyFolders($project), ]); } + + /** + * Detect sample folders that are nested inside another sample folder. + * + * A "sample folder" is any FileSystemObject that nmrXiv promotes to a + * Study (because its descendants contain raw or processed instrument + * data). Nesting one sample folder inside another breaks the one-sample + * = one-folder contract that ArchiveStudy and the NMRium importer rely + * on, so we surface a non-blocking warning to the user. + * + * @return array + */ + public function detectNestedStudyFolders(Project $project): array + { + $studies = $project->studies()->with('fsObject')->get(); + + $studyFsById = []; + foreach ($studies as $study) { + if ($study->fsObject) { + $studyFsById[$study->fsObject->id] = $study; + } + } + + if (count($studyFsById) < 2) { + return []; + } + + $message = 'Please make sure you have datasets associated with one sample in one folder.'; + $reportedPairs = []; + $warnings = []; + + foreach ($studyFsById as $fsId => $study) { + $fs = $study->fsObject; + $cursor = $fs->parent; + while ($cursor) { + if (isset($studyFsById[$cursor->id]) && $cursor->id !== $fs->id) { + $ancestor = $studyFsById[$cursor->id]; + $pairKey = $ancestor->fsObject->id.':'.$fs->id; + if (! isset($reportedPairs[$pairKey])) { + $reportedPairs[$pairKey] = true; + $warnings[] = sprintf( + 'Sample folder "%s" is nested inside sample folder "%s". %s', + $fs->name, + $ancestor->fsObject->name, + $message + ); + } + break; + } + $cursor = $cursor->parent; + } + } + + return $warnings; + } } diff --git a/app/Actions/Draft/ResetSampleFolder.php b/app/Actions/Draft/ResetSampleFolder.php new file mode 100644 index 000000000..c96261eed --- /dev/null +++ b/app/Actions/Draft/ResetSampleFolder.php @@ -0,0 +1,138 @@ +draft_id !== $draft->id) { + return [ + 'ok' => false, + 'study_id' => null, + 'message' => 'Filesystem object does not belong to this draft.', + ]; + } + + if ($folder->model_type !== 'study') { + return [ + 'ok' => false, + 'study_id' => null, + 'message' => 'Only sample folders can be reset.', + ]; + } + + $study = $this->resolveStudy($folder); + $studyId = $study?->id; + + DB::transaction(function () use ($folder, $study): void { + $this->clearSubtreeTags($folder); + + if ($study) { + $this->resetStudyCachedState->forStudy($study); + } + }); + + Log::info('ResetSampleFolder: reset sample folder', [ + 'draft_id' => $draft->id, + 'fso_id' => $folder->id, + 'study_id' => $studyId, + ]); + + return [ + 'ok' => true, + 'study_id' => $studyId, + 'message' => 'Sample folder reset. The next "Proceed to Step 2" run will reprocess it.', + ]; + } + + /** + * Recursively clear `model_type`, `instrument_type` and `is_processed` + * on the sample folder and every descendant so the next ProcessDraft + * run re-walks and re-tags the entire subtree from scratch. + * + * Uses `saveQuietly()` to bypass FileSystemObjectObserver, which would + * otherwise re-trigger archive invalidation work and dispatch an + * ArchiveStudy job we explicitly want to defer to Step 2. + */ + private function clearSubtreeTags(FileSystemObject $folder, int $depth = 0): void + { + if ($depth >= self::MAX_DEPTH) { + return; + } + + $changed = false; + if ($folder->model_type !== null) { + $folder->model_type = null; + $changed = true; + } + if ($folder->instrument_type !== null) { + $folder->instrument_type = null; + $changed = true; + } + if ($folder->is_processed) { + $folder->is_processed = false; + $changed = true; + } + if ($changed) { + $folder->saveQuietly(); + } + + foreach ($folder->children as $child) { + $this->clearSubtreeTags($child, $depth + 1); + } + } + + /** + * Resolve the Study attached to a sample-folder FSO. + */ + private function resolveStudy(FileSystemObject $folder): ?Study + { + if ($folder->study_id) { + return Study::find($folder->study_id); + } + + return Study::where('fs_id', $folder->id)->first(); + } +} diff --git a/app/Actions/Draft/UserDrafts.php b/app/Actions/Draft/UserDrafts.php index c3f7246da..f9034f1c7 100644 --- a/app/Actions/Draft/UserDrafts.php +++ b/app/Actions/Draft/UserDrafts.php @@ -15,7 +15,10 @@ public function execute(User $user): Collection { [$user_id, $team_id] = $user->getUserTeamData(); - return Draft::with('Tags') + return Draft::with([ + 'Tags', + 'project:id,slug,status,draft_id', + ]) ->where('owner_id', $user_id) ->where('team_id', $team_id) ->where('is_deleted', false) @@ -28,7 +31,7 @@ public function execute(User $user): Collection } /** - * Find existing default draft without files. + * Find existing default draft without files for the user's current team. */ public function findDefaultDraft(User $user): ?Draft { @@ -36,6 +39,7 @@ public function findDefaultDraft(User $user): ?Draft return Draft::doesntHave('files') ->where('owner_id', $user_id) + ->where('team_id', $team_id) ->first(); } diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php index 39ed30dea..683d5755d 100644 --- a/app/Actions/Fortify/CreateNewUser.php +++ b/app/Actions/Fortify/CreateNewUser.php @@ -17,7 +17,7 @@ class CreateNewUser implements CreatesNewUsers /** * Create a newly registered user. * - * @return \App\Models\User + * @return User */ public function create(array $input) { @@ -39,6 +39,7 @@ public function create(array $input) 'username' => $input['username'], 'orcid_id' => $input['orcid_id'], 'affiliation' => $input['affiliation'], + 'ror_id' => $input['ror_id'] ?? null, 'password' => Hash::make($input['password']), ]), function (User $user) { diff --git a/app/Actions/Fortify/UpdateUserProfileInformation.php b/app/Actions/Fortify/UpdateUserProfileInformation.php index a057a9ec2..c3fa7a35b 100644 --- a/app/Actions/Fortify/UpdateUserProfileInformation.php +++ b/app/Actions/Fortify/UpdateUserProfileInformation.php @@ -25,7 +25,8 @@ public function update($user, array $input) 'username' => ['required', 'string', 'max:255', Rule::unique('users')->ignore($user->id)], 'photo' => ['nullable', 'mimes:jpg,jpeg,png', 'max:1024'], 'orcid_id' => ['nullable', 'string', 'max:255'], - 'affiliation' => ['nullable', 'string', 'max:255'], + 'affiliation' => ['nullable', 'string'], + 'ror_id' => ['nullable', 'string', 'max:255'], ])->validateWithBag('updateProfileInformation'); if (isset($input['photo'])) { @@ -44,6 +45,7 @@ public function update($user, array $input) 'email' => $input['email'], 'orcid_id' => $input['orcid_id'], 'affiliation' => $input['affiliation'] ? $input['affiliation'] : null, + 'ror_id' => $input['ror_id'] ?? null, ])->save(); } } @@ -64,6 +66,7 @@ protected function updateVerifiedUser($user, array $input) 'email' => $input['email'], 'orcid_id' => $input['orcid_id'], 'affiliation' => $input['affiliation'], + 'ror_id' => $input['ror_id'] ?? null, ])->save(); $user->sendEmailVerificationNotification(); diff --git a/app/Actions/Jetstream/AddTeamMember.php b/app/Actions/Jetstream/AddTeamMember.php index 2e3b8a6f4..9221bbf61 100644 --- a/app/Actions/Jetstream/AddTeamMember.php +++ b/app/Actions/Jetstream/AddTeamMember.php @@ -48,7 +48,7 @@ protected function validate($team, string $email, ?string $role) 'email' => $email, 'role' => $role, ], $this->rules(), [ - 'email.exists' => __('We were unable to find a registered user with this email address.'), + 'email.exists' => __('Unable to add member with this email address.'), ])->after( $this->ensureUserIsNotAlreadyOnTeam($team, $email) )->validateWithBag('addTeamMember'); @@ -81,7 +81,7 @@ protected function ensureUserIsNotAlreadyOnTeam($team, string $email) $validator->errors()->addIf( $team->hasUserWithEmail($email), 'email', - __('This user already belongs to the team.') + __('Unable to add member with this email address.') ); }; } diff --git a/app/Actions/Jetstream/DeleteUser.php b/app/Actions/Jetstream/DeleteUser.php index 377be6e3f..99b3054ac 100644 --- a/app/Actions/Jetstream/DeleteUser.php +++ b/app/Actions/Jetstream/DeleteUser.php @@ -11,7 +11,7 @@ class DeleteUser implements DeletesUsers /** * The team deleter implementation. * - * @var \Laravel\Jetstream\Contracts\DeletesTeams + * @var DeletesTeams */ protected $deletesTeams; diff --git a/app/Actions/Project/ArchiveProject.php b/app/Actions/Project/ArchiveProject.php index c08b6f5d3..68ed18a56 100644 --- a/app/Actions/Project/ArchiveProject.php +++ b/app/Actions/Project/ArchiveProject.php @@ -12,14 +12,24 @@ class ArchiveProject * @param mixed $project * @return void */ - public function toggle($project) + public function toggleArchive($project) { $archiveState = ! $project->is_archived; - $project->studies()->update(['is_archived' => $archiveState]); + $project->studies()->update([ + 'is_archived' => $archiveState, + 'status' => $archiveState ? 'archived' : 'published', + ]); + foreach ($project->studies as $study) { - $study->datasets()->update(['is_archived' => $archiveState]); + $study->datasets()->update([ + 'is_archived' => $archiveState, + 'status' => $archiveState ? 'archived' : 'published', + ]); } + $project->is_archived = $archiveState; + $project->status = $archiveState ? 'archived' : 'published'; + if ($project->is_archived) { $project->sendNotification('archival', $this->prepareSendList($project)); } @@ -34,15 +44,19 @@ public function toggle($project) */ public function prepareSendList($project) { - $sendTo = []; - foreach ($project->allUsers() as $member) { - if ($member->projectMembership->role == 'creator' || $member->projectMembership->role == 'owner') { - array_push($sendTo, $member); - } else { - array_push($sendTo, $project->owner); + $sendTo = collect(); + + if ($project->owner) { + $sendTo->push($project->owner); + } + + foreach ($project->users as $member) { + $role = $member->projectMembership?->role; + if ($role === 'creator' || $role === 'owner') { + $sendTo->push($member); } } - return $sendTo; + return $sendTo->unique('id')->values()->all(); } } diff --git a/app/Actions/Project/CreateNewProject.php b/app/Actions/Project/CreateNewProject.php index 8de33cca1..6d3e24aad 100644 --- a/app/Actions/Project/CreateNewProject.php +++ b/app/Actions/Project/CreateNewProject.php @@ -15,11 +15,11 @@ class CreateNewProject /** * Create a project. * - * @return \App\Models\Project + * @return Project */ public function create(array $input) { - $license = $input['license']; + $license = $input['license'] ?? null; $errorMessages = [ 'license.required_if' => 'The license field is required when the project is made public.', ]; diff --git a/app/Actions/Project/DeleteProject.php b/app/Actions/Project/DeleteProject.php index ba0272734..ced0259a5 100644 --- a/app/Actions/Project/DeleteProject.php +++ b/app/Actions/Project/DeleteProject.php @@ -6,6 +6,7 @@ use App\Models\Project; use App\Models\User; use Illuminate\Support\Carbon; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Storage; use Throwable; @@ -30,8 +31,8 @@ public function delete($project) } else { $project->studies()->update(['is_deleted' => true]); foreach ($project->studies as $study) { - $study->update(['is_deleted' => true]); - $study->datasets()->update(['is_deleted' => true]); + $study->update(['is_deleted' => true, 'status' => 'deleted']); + $study->datasets()->update(['is_deleted' => true, 'status' => 'deleted']); } $draft = $project->draft; if ($draft) { @@ -40,6 +41,7 @@ public function delete($project) $project->name = $project->name; $project->deleted_on = Carbon::now(); $project->is_deleted = true; + $project->status = 'deleted'; $project->sendNotification('deletion', $this->prepareSendList($project)); } $project->save(); @@ -106,7 +108,7 @@ public function deleteStudy($study) /** * Delete dataset and related objects. * - * @param \Illuminate\Support\Collection $datasets + * @param Collection $datasets * @return void */ public function deleteDatasets($dataset) @@ -204,9 +206,9 @@ public function deleteFSO(FileSystemObject $filesystemobject) $fsoIds = $this->getChildrenIds($filesystemobject, []); if (Storage::has($filesystemobject->path)) { if ($filesystemobject->type == 'directory') { - Storage::disk(env('FILESYSTEM_DRIVER'))->deleteDirectory($filesystemobject->path); + Storage::disk(config('filesystems.default'))->deleteDirectory($filesystemobject->path); } else { - Storage::disk(env('FILESYSTEM_DRIVER'))->delete($filesystemobject->path); + Storage::disk(config('filesystems.default'))->delete($filesystemobject->path); } FileSystemObject::whereIn('id', $fsoIds)->delete(); } @@ -254,15 +256,19 @@ public function deleteSample($sample) */ public function prepareSendList($project) { - $sendTo = []; - foreach ($project->allUsers() as $member) { - if ($member->projectMembership->role == 'creator' || $member->projectMembership->role == 'owner') { - array_push($sendTo, $member); - } else { - array_push($sendTo, $project->owner); + $sendTo = collect(); + + if ($project->owner) { + $sendTo->push($project->owner); + } + + foreach ($project->users as $member) { + $role = $member->projectMembership?->role; + if ($role === 'creator' || $role === 'owner') { + $sendTo->push($member); } } - return $sendTo; + return $sendTo->unique('id')->values()->all(); } } diff --git a/app/Actions/Project/PublishEmbargoProject.php b/app/Actions/Project/PublishEmbargoProject.php new file mode 100644 index 000000000..078680456 --- /dev/null +++ b/app/Actions/Project/PublishEmbargoProject.php @@ -0,0 +1,129 @@ +is_public) { + throw ValidationException::withMessages([ + 'publish' => 'Project is already public.', + ]); + } + + if ($project->is_archived) { + throw ValidationException::withMessages([ + 'publish' => 'Archived projects cannot be published.', + ]); + } + + if ($project->status !== 'embargo') { + throw ValidationException::withMessages([ + 'publish' => 'Project is not in embargo status.', + ]); + } + + if ($project->doi === null || $project->doi === '') { + throw ValidationException::withMessages([ + 'publish' => 'A DOI is required before publishing this project.', + ]); + } + + $validation = $this->validateForPublication($project, $restoreReleaseDateOnValidationFailure); + $releaseDate = now()->startOfDay()->toDateString(); + $hasDraft = $project->draft_id !== null; + + DB::transaction(function () use ($project, $releaseDate) { + if ($releaseDate !== null) { + $project->release_date = $releaseDate; + } + + $project->status = 'queued'; + $project->save(); + }); + + Log::info('embargo_publish_trace', [ + 'stage' => 'publish_embargo_project_action_dispatch_process_submission', + 'project_id' => $project->id, + 'release_date' => $project->release_date, + 'status' => $project->status, + 'draft_id' => $project->draft_id, + 'dispatch' => $hasDraft ? 'async' : 'sync', + ]); + + if ($hasDraft) { + ProcessSubmission::dispatch($project); + + return [ + 'hasDraft' => true, + 'dispatched' => 'async', + 'validation' => $validation, + ]; + } + + ProcessSubmission::dispatchSync($project); + + return [ + 'hasDraft' => false, + 'dispatched' => 'sync', + 'validation' => $validation, + ]; + } + + private function validateForPublication(Project $project, bool $restoreReleaseDateOnValidationFailure): Validation + { + $validation = $project->validation; + + if (! $validation) { + throw ValidationException::withMessages([ + 'publish' => 'Project validation not found. Please ensure the project is properly configured.', + ]); + } + + $originalReleaseDate = $project->release_date; + + $project->release_date = now()->startOfDay()->toDateString(); + $project->save(); + + $validation->process(); + $publishAttemptValidation = $validation->fresh(); + + if (! $publishAttemptValidation['report']['project']['status']) { + if ($restoreReleaseDateOnValidationFailure) { + $project->release_date = $originalReleaseDate; + $project->save(); + $project->refresh(); + + if ($project->validation) { + $project->validation->process(); + } + } + + throw new EmbargoPublicationFailed( + $project, + 'Validation failing. Please provide all the required data and try again. If the problem persists, please contact us at info.nmrxiv@uni-jena.de', + $publishAttemptValidation, + ); + } + + return $publishAttemptValidation; + } +} diff --git a/app/Actions/Project/RestoreProject.php b/app/Actions/Project/RestoreProject.php index e6d0564ad..1f119d9cf 100644 --- a/app/Actions/Project/RestoreProject.php +++ b/app/Actions/Project/RestoreProject.php @@ -27,8 +27,10 @@ public function restore($project) $study->datasets()->update(['is_deleted' => false]); } $draft = $project->draft; - $draft->is_deleted = false; - $draft->save(); + if ($draft) { + $draft->is_deleted = false; + $draft->save(); + } $project->is_deleted = false; $project->save(); } diff --git a/app/Actions/Project/UpdateProject.php b/app/Actions/Project/UpdateProject.php index aa9433412..fa1fd6be6 100644 --- a/app/Actions/Project/UpdateProject.php +++ b/app/Actions/Project/UpdateProject.php @@ -2,7 +2,9 @@ namespace App\Actions\Project; +use App\Actions\Citation\SyncCitationPivot; use App\Models\Project; +use App\Models\User; use App\Models\Validation; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Storage; @@ -15,17 +17,19 @@ class UpdateProject /** * Create a project. * - * @return \App\Models\Project + * @return Project */ public function update(Project $project, array $input) { $errorMessages = [ 'license.required_if' => 'The license field is required when the project is made public.', + 'photo.mimes' => 'The project image must be a file of type: jpg, jpeg, png, gif, webp.', ]; Validator::make($input, [ 'name' => ['required', 'string', 'max:255', Rule::unique('projects') ->where('owner_id', $project->owner_id)->ignore($project->id), ], 'license' => ['required_if:is_public,"true"'], + 'photo' => ['nullable', 'mimes:jpg,jpeg,png,gif,webp', 'max:2048'], ], $errorMessages)->validate(); return DB::transaction(function () use ($input, $project) { @@ -34,7 +38,7 @@ public function update(Project $project, array $input) if (array_key_exists('photo', $input)) { $image = $input['photo']; if (! is_null($image)) { - $s3 = Storage::disk(env('FILESYSTEM_DRIVER_PUBLIC')); + $s3 = Storage::disk(config('filesystems.default_public')); $file_name = uniqid().'.'.$image->getClientOriginalExtension(); $s3filePath = '/projects/'.$file_name; $s3->put($s3filePath, file_get_contents($image), 'public'); @@ -46,6 +50,23 @@ public function update(Project $project, array $input) $licenseExists = array_key_exists('license_id', $input); $license_id = $licenseExists ? $input['license_id'] : $project->license_id; + $projectSpeciesUpdated = isset($input['project_species_updated']) + && filter_var($input['project_species_updated'], FILTER_VALIDATE_BOOLEAN); + + $speciesValue = $project->species; + if ($projectSpeciesUpdated) { + $raw = $input['species'] ?? []; + if (is_array($raw)) { + $speciesValue = json_encode($raw); + } elseif (is_string($raw)) { + $speciesValue = $raw; + } else { + $speciesValue = json_encode([]); + } + } elseif (array_key_exists('species', $input)) { + $speciesValue = $input['species']; + } + $project ->forceFill([ 'name' => array_key_exists('name', $input) ? $input['name'] : $project->name, @@ -72,13 +93,20 @@ public function update(Project $project, array $input) 'team_id' => array_key_exists('team_id', $input) ? $input['team_id'] : $project->team_id, 'owner_id' => array_key_exists('owner_id', $input) ? $input['owner_id'] : $project->owner_id, 'license_id' => $license_id, - 'species' => array_key_exists('species', $input) ? $input['species'] : $project->species, + 'species' => $speciesValue, 'release_date' => $release_date, 'project_photo_path' => $s3filePath ? $s3filePath : $project->project_photo_path, ]) ->save(); - if (array_key_exists('tags_array', $input)) { + $projectTagsUpdated = isset($input['project_tags_updated']) + && filter_var($input['project_tags_updated'], FILTER_VALIDATE_BOOLEAN); + + if ($projectTagsUpdated) { + $raw = $input['tags_array'] ?? []; + $tagsArray = is_array($raw) ? $raw : []; + $project->syncTagsWithType($tagsArray, 'Project'); + } elseif (array_key_exists('tags_array', $input)) { $project->syncTagsWithType($input['tags_array'], 'Project'); } @@ -188,20 +216,13 @@ public function updateContributorType(Project $project, $author_id, $role) /** * Attach citations to a project. * - * @param \App\Models\User $user + * @param User $user * @param array $citations * @return void */ public function syncCitations(Project $project, $citations, $user) { - $citations_map = []; - foreach ($citations as $citation) { - $citations_map[$citation->id] = ['user' => $user->id]; - } - - $project->citations()->sync( - $citations_map - ); + app(SyncCitationPivot::class)->sync($project, $citations, $user); } /** @@ -212,8 +233,6 @@ public function syncCitations(Project $project, $citations, $user) */ public function detachCitation(Project $project, $citation_id) { - $project->citations()->detach( - $citation_id - ); + app(SyncCitationPivot::class)->detach($project, (int) $citation_id); } } diff --git a/app/Actions/Study/AddStudyMember.php b/app/Actions/Study/AddStudyMember.php index dfedef564..2a5fe13b0 100644 --- a/app/Actions/Study/AddStudyMember.php +++ b/app/Actions/Study/AddStudyMember.php @@ -47,7 +47,7 @@ protected function validate($study, string $email, ?string $role) 'email' => $email, 'role' => $role, ], $this->rules(), [ - 'email.exists' => __('We were unable to find a registered user with this email address.'), + 'email.exists' => __('Unable to add member with this email address.'), ])->after( $this->ensureUserIsNotAlreadyOnStudy($study, $email) )->validateWithBag('addStudyMember'); @@ -80,7 +80,7 @@ protected function ensureUserIsNotAlreadyOnStudy($study, string $email) $validator->errors()->addIf( $study->hasUserWithEmail($email), 'email', - __('This user already belongs to the study.') + __('Unable to add member with this email address.') ); }; } diff --git a/app/Actions/Study/CreateNewStudy.php b/app/Actions/Study/CreateNewStudy.php index 2ad507e89..7ffd828ec 100644 --- a/app/Actions/Study/CreateNewStudy.php +++ b/app/Actions/Study/CreateNewStudy.php @@ -14,7 +14,7 @@ class CreateNewStudy /** * Create a study. * - * @return \App\Models\Study + * @return Study */ public function create(array $input) { diff --git a/app/Actions/Study/UpdateStudy.php b/app/Actions/Study/UpdateStudy.php index 5fc4ff16c..3aa600b5d 100644 --- a/app/Actions/Study/UpdateStudy.php +++ b/app/Actions/Study/UpdateStudy.php @@ -4,6 +4,7 @@ use App\Models\Study; use App\Models\Ticker; +use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; @@ -13,7 +14,7 @@ class UpdateStudy /** * Create a study. * - * @return \App\Models\Study + * @return Study */ public function update(Study $study, array $input) { @@ -29,7 +30,7 @@ public function update(Study $study, array $input) $study->forceFill([ 'name' => $input['name'], 'slug' => Str::slug($input['name'], '-'), - 'description' => $input['description'] ? $input['description'] : $study->description, + 'description' => array_key_exists('description', $input) ? $input['description'] : $study->description, 'color' => array_key_exists('color', $input) ? $input['color'] : $study->color, 'starred' => array_key_exists('starred', $input) ? $input['starred'] : $study->starred, 'location' => array_key_exists('location', $input) ? $input['location'] : $study->location, @@ -56,30 +57,32 @@ public function update(Study $study, array $input) $release_date = Carbon::now()->timestamp; $sample = $study->sample; - $sampleIdentifier = $sample->identifier ? $sample->identifier : null; + if ($sample) { + $sampleIdentifier = $sample->identifier ? $sample->identifier : null; - if ($sampleIdentifier == null) { - $sampleTicker = Ticker::whereType('sample')->first(); - $sampleIdentifier = $sampleTicker->index + 1; - $sample->identifier = $sampleIdentifier; - $sample->save(); + if ($sampleIdentifier == null) { + $sampleTicker = Ticker::whereType('sample')->first(); + $sampleIdentifier = $sampleTicker->index + 1; + $sample->identifier = $sampleIdentifier; + $sample->save(); - $sampleTicker->index = $sampleIdentifier; - $sampleTicker->save(); - } + $sampleTicker->index = $sampleIdentifier; + $sampleTicker->save(); + } - $molecules = $sample->molecules; + $molecules = $sample->molecules; - foreach ($molecules as $molecule) { - $moleculeIdentifier = $molecule->identifier ? $molecule->identifier : null; - if ($moleculeIdentifier == null) { - $moleculeTicker = Ticker::whereType('molecule')->first(); - $moleculeIdentifier = $moleculeTicker->index + 1; - $molecule->identifier = $moleculeIdentifier; - $molecule->save(); + foreach ($molecules as $molecule) { + $moleculeIdentifier = $molecule->identifier ? $molecule->identifier : null; + if ($moleculeIdentifier == null) { + $moleculeTicker = Ticker::whereType('molecule')->first(); + $moleculeIdentifier = $moleculeTicker->index + 1; + $molecule->identifier = $moleculeIdentifier; + $molecule->save(); - $moleculeTicker->index = $moleculeIdentifier; - $moleculeTicker->save(); + $moleculeTicker->index = $moleculeIdentifier; + $moleculeTicker->save(); + } } } } diff --git a/app/Console/Commands/ArchiveData.php b/app/Console/Commands/ArchiveData.php index 738e1a74b..986be531e 100644 --- a/app/Console/Commands/ArchiveData.php +++ b/app/Console/Commands/ArchiveData.php @@ -7,6 +7,7 @@ use App\Models\Project; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; class ArchiveData extends Command { @@ -38,6 +39,13 @@ public function handle(): void foreach ($projects as $project) { echo $project->identifier; echo "\r\n"; + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_data_command_dispatch', + 'project_id' => $project->id, + 'identifier' => $project->identifier, + ]); + ArchiveProject::dispatch($project); ArchiveStudy::dispatch($project); } diff --git a/app/Console/Commands/AssignDOIs.php b/app/Console/Commands/AssignDOIs.php index 2bb716214..9f49c6e72 100644 --- a/app/Console/Commands/AssignDOIs.php +++ b/app/Console/Commands/AssignDOIs.php @@ -3,6 +3,7 @@ namespace App\Console\Commands; use App\Actions\Project\AssignIdentifier; +use App\Actions\Project\UpdateDOI; use App\Models\Project; use App\Models\Study; use Illuminate\Console\Command; @@ -15,40 +16,130 @@ class AssignDOIs extends Command * * @var string */ - protected $signature = 'nmrxiv:assign-dois'; + protected $signature = 'nmrxiv:assign-dois + {--project= : Generate DOIs for a single public project by ID (and its studies/datasets)} + {--study= : Generate DOIs for a single public study by ID}'; /** * The console command description. * * @var string */ - protected $description = 'Assigns dois to all unassigned public models'; + protected $description = 'Create DataCite DOIs for public records missing a DOI, or target one project/study by ID'; /** * Execute the console command. */ - public function handle(AssignIdentifier $assigner) + public function handle(AssignIdentifier $assigner, UpdateDOI $updater): int { - return DB::transaction(function () use ($assigner) { - $projects = Project::where([ - ['is_public', true], - ['doi', null], - ])->get(); + $projectId = $this->option('project'); + $studyId = $this->option('study'); + + if ($projectId !== null && $projectId !== '') { + return $this->assignForProject($assigner, $updater, (int) $projectId); + } + + if ($studyId !== null && $studyId !== '') { + return $this->assignForStudy($assigner, $updater, (int) $studyId); + } + + return $this->assignBatch($assigner, $updater); + } + + private function assignForProject(AssignIdentifier $assigner, UpdateDOI $updater, int $projectId): int + { + $project = Project::query()->find($projectId); + + if (! $project) { + $this->error("No project found with id [{$projectId}]."); + + return self::FAILURE; + } + + if (! $project->is_public) { + $this->warn("Project [{$projectId}] is not public; skipping (DOI minting is for public resources)."); + + return self::SUCCESS; + } + + if ($project->doi !== null) { + $this->warn("Project [{$projectId}] already has DOI [{$project->doi}]; skipping."); + + return self::SUCCESS; + } + + return DB::transaction(function () use ($assigner, $updater, $project) { + $assigner->assign($project); + $updater->update($project->fresh()); + + $this->info("Assigned DOI metadata for project [{$project->id}] ({$project->name})."); + + return self::SUCCESS; + }); + } + + private function assignForStudy(AssignIdentifier $assigner, UpdateDOI $updater, int $studyId): int + { + $study = Study::query()->find($studyId); + + if (! $study) { + $this->error("No study found with id [{$studyId}]."); + + return self::FAILURE; + } + + if (! $study->is_public) { + $this->warn("Study [{$studyId}] is not public; skipping."); + + return self::SUCCESS; + } + + if ($study->doi !== null) { + $this->warn("Study [{$studyId}] already has DOI [{$study->doi}]; skipping."); + + return self::SUCCESS; + } + + return DB::transaction(function () use ($assigner, $updater, $study) { + $assigner->assign(collect([$study])); + $updater->update(collect([$study->fresh()])); + + $this->info("Assigned DOI metadata for study [{$study->id}] ({$study->name})."); + + return self::SUCCESS; + }); + } + + private function assignBatch(AssignIdentifier $assigner, UpdateDOI $updater): int + { + return DB::transaction(function () use ($assigner, $updater) { + $projects = Project::query() + ->where('is_public', true) + ->whereNull('doi') + ->get(); foreach ($projects as $project) { - $projectDOI = $project->doi ? $project->doi : null; $assigner->assign($project); + $updater->update($project->fresh()); + $this->line("Project [{$project->id}]: DOIs assigned / metadata updated."); } - $studies = Study::where([ - ['is_public', true], - ['doi', null], - ])->get(); + $studies = Study::query() + ->where('is_public', true) + ->whereNull('doi') + ->get(); foreach ($studies as $study) { - $studyDOI = $study->doi ? $study->doi : null; $assigner->assign(collect([$study])); + $updater->update(collect([$study->fresh()])); + $this->line("Study [{$study->id}]: DOIs assigned / metadata updated."); + } + + if ($projects->isEmpty() && $studies->isEmpty()) { + $this->info('No public projects or studies without a DOI were found.'); } + + return self::SUCCESS; }); } } diff --git a/app/Console/Commands/ExtractSpectra.php b/app/Console/Commands/ExtractSpectra.php deleted file mode 100644 index caf108f9f..000000000 --- a/app/Console/Commands/ExtractSpectra.php +++ /dev/null @@ -1,182 +0,0 @@ -get(); - - foreach ($projects as $project) { - echo "\r\n"; - echo $project->identifier; - echo "\r\n"; - $studies = $project->studies; - foreach ($studies as $study) { - echo $study->identifier; - echo "\r\n"; - try { - if (! $study->has_nmrium) { - DB::transaction(function () use ($study) { - $download_url = $study->download_url; - if ($download_url) { - $nmrium_ = $this->processSpectra($download_url); - $parsedSpectra = $nmrium_['data']; - foreach ($parsedSpectra['spectra'] as $spectra) { - unset($spectra['data']); - unset($spectra['meta']); - unset($spectra['originalData']); - unset($spectra['originalInfo']); - } - - $version = $parsedSpectra['version']; - unset($parsedSpectra['version']); - - $nmriumJSON = [ - 'data' => $parsedSpectra, - 'version' => $version, - ]; - - $nmrium = $study->nmrium; - - if ($nmrium) { - $nmrium->nmrium_info = json_encode($nmriumJSON, JSON_UNESCAPED_UNICODE); - $nmrium->save(); - } else { - $nmrium = NMRium::create([ - 'nmrium_info' => json_encode($nmriumJSON, JSON_UNESCAPED_UNICODE), - ]); - $study->nmrium()->save($nmrium); - $study->has_nmrium = true; - $study->save(); - } - } - }); - } - $study = $study->fresh(); - if ($study->has_nmrium) { - $nmriumInfo = $study->nmrium->nmrium_info; - if (count($nmriumInfo['data']['spectra']) == 0) { - echo '--MISSING SPECTRA INFO (NMRIUM JSON)--'; - echo "\r\n"; - } else { - foreach ($study->datasets as $dataset) { - echo $dataset->identifier; - echo "\r\n"; - // echo $dataset->type; - // echo "\r\n"; - if (! $dataset->has_nmrium) { - $nmriumInfo = $study->nmrium->nmrium_info; - $_nmriumJSON = $nmriumInfo; - $fsObject = $dataset->fsObject; - - $studyFSObject = $study->fsObject; - $datasetFSObject = $dataset->fsObject; - - $draft = $study->draft; - - if ($draft && $draft->eln == 'chemotion') { - $path = '/'.$studyFSObject->name.'/'.$datasetFSObject->parent->name.'/'.$datasetFSObject->name; - } else { - $path = '/'.$studyFSObject->name.'/'.$datasetFSObject->name; - } - - $fType = $studyFSObject->type; - - $pathsMatch = false; - $spectrum = []; - $type = []; - foreach ($nmriumInfo['data']['spectra'] as $spectra) { - unset($_nmriumJSON['data']['spectra']); - $files = $spectra['sourceSelector']['files']; - if ($files) { - foreach ($files as $file) { - if (str_contains($file, $fType == 'file' ? $path : $path.'/')) { - $pathsMatch = true; - } - } - } - if ($pathsMatch) { - array_push($spectrum, $spectra); - $experimentDetailsExists = array_key_exists('experiment', $spectra['info']); - if ($experimentDetailsExists) { - $experiment = $spectra['info']['experiment']; - $nucleus = $spectra['info']['nucleus']; - if (is_array($nucleus)) { - $nucleus = implode('-', $nucleus); - } - array_push($type, $experiment.' - '.$nucleus); - } - $pathsMatch = false; - } - } - if (count($spectrum) > 0) { - $_nmriumJSON['data']['spectra'] = $spectrum; - $_nmrium = $dataset->nmrium; - if ($_nmrium) { - $_nmrium->nmrium_info = json_encode($_nmriumJSON, JSON_UNESCAPED_UNICODE); - $dataset->has_nmrium = true; - $_nmrium->save(); - } else { - $_nmrium = NMRium::create([ - 'nmrium_info' => json_encode($_nmriumJSON, JSON_UNESCAPED_UNICODE), - ]); - $dataset->nmrium()->save($_nmrium); - $dataset->has_nmrium = true; - } - } - $uType = array_unique($type); - if (count($uType) == 1) { - $dataset->type = $uType[0]; - } - $dataset->save(); - } - } - } - } - } catch (Exception $e) { - echo 'Caught exception: ', $e->getMessage(), "\n"; - } - - } - } - } - - public function processSpectra($url) - { - $url = urlencode($url); - $response = Http::timeout(300)->post('https://nodejs.nmrxiv.org/spectra-parser', [ - 'urls' => [$url], - 'snapshot' => false, - ]); - - return $response->json(); - } -} diff --git a/app/Console/Commands/ManageFiles.php b/app/Console/Commands/ManageFiles.php index 7ae4d64d5..384b984da 100644 --- a/app/Console/Commands/ManageFiles.php +++ b/app/Console/Commands/ManageFiles.php @@ -7,6 +7,8 @@ use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Storage; +use League\Flysystem\DirectoryAttributes; +use League\Flysystem\FileAttributes; class ManageFiles extends Command { @@ -43,10 +45,10 @@ public function handle(): void public function processFiles($path) { - $listing = Storage::disk(env('FILESYSTEM_DRIVER'))->listContents($path, true); + $listing = Storage::disk(config('filesystems.default'))->listContents($path, true); foreach ($listing as $item) { $path = $item->path(); - if ($item instanceof \League\Flysystem\FileAttributes) { + if ($item instanceof FileAttributes) { $fsObject = FileSystemObject::where([ ['path', '/'.$path], ])->first(); @@ -57,7 +59,7 @@ public function processFiles($path) $fsObject->info = $fsInfo; $fsObject->save(); } - } elseif ($item instanceof \League\Flysystem\DirectoryAttributes) { + } elseif ($item instanceof DirectoryAttributes) { // echo $item->study_id; } } diff --git a/app/Console/Commands/PublishEmbargoProjects.php b/app/Console/Commands/PublishEmbargoProjects.php new file mode 100644 index 000000000..694bf6f1e --- /dev/null +++ b/app/Console/Commands/PublishEmbargoProjects.php @@ -0,0 +1,182 @@ +sendReminders($now->copy()->addDays(7), 7, $remindersSent); + + // Send 3 days reminders + $this->sendReminders($now->copy()->addDays(3), 3, $remindersSent); + + // Send 1 day reminders + $this->sendReminders($now->copy()->addDays(1), 1, $remindersSent); + + // Publish projects that have reached their release date + $publishedCount = $this->publishReadyProjects($embargoPublisher, $now); + + // Clean up old reminder records for published projects + $this->cleanupOldReminders(); + + $this->info("Sent {$remindersSent} embargo release reminders."); + $this->info("Published {$publishedCount} embargo projects."); + + return Command::SUCCESS; + } + + /** + * Send reminder notifications for projects approaching release date + */ + private function sendReminders(Carbon $targetDate, int $daysUntilRelease, int &$remindersSent): void + { + $projects = Project::where([ + ['status', 'embargo'], + ['is_public', false], + ['is_archived', false], + ])->whereNotNull('release_date') + ->whereNotNull('identifier') + ->whereDate('release_date', $targetDate->toDateString()) + ->whereDoesntHave('embargoReminders', function ($query) use ($daysUntilRelease) { + $query->where('days_before_release', $daysUntilRelease); + }) + ->with('owner') + ->get(); + + foreach ($projects as $project) { + try { + $this->info("Sending {$daysUntilRelease} day reminder for project: {$project->name} (ID: {$project->id})"); + + // Send reminder notification + Notification::send($project->owner, new EmbargoReleaseReminderNotification($project, $daysUntilRelease)); + + // Record that reminder was sent + EmbargoReminder::create([ + 'project_id' => $project->id, + 'days_before_release' => $daysUntilRelease, + 'sent_at' => Carbon::now(), + ]); + + $remindersSent++; + $this->info("Successfully sent {$daysUntilRelease} day reminder for: {$project->name}"); + + } catch (\Exception $e) { + $this->error("Failed to send reminder for project {$project->name}: {$e->getMessage()}"); + } + } + } + + /** + * Publish projects that have reached their release date + */ + private function publishReadyProjects(PublishEmbargoProject $embargoPublisher, Carbon $now): int + { + $this->info("Looking for projects to publish with release_date <= {$now->toDateString()}"); + + $projects = Project::where([ + ['status', 'embargo'], + ['is_public', false], + ['is_archived', false], + ])->whereNotNull('release_date') + ->whereNotNull('identifier') + ->whereDate('release_date', '<=', $now->toDateString()) + ->get(); + + $this->info("Found {$projects->count()} projects eligible for publishing"); + + $publishedCount = 0; + + foreach ($projects as $project) { + try { + $this->info("Publishing embargo project: {$project->name} (ID: {$project->id})"); + + $embargoPublisher->publish($project, restoreReleaseDateOnValidationFailure: true); + + $publishedCount++; + $this->info("Successfully queued project for publication: {$project->name}"); + + } catch (EmbargoPublicationFailed $e) { + $this->error("Failed to publish project {$project->name}: {$e->getMessage()}"); + $this->notifyPublicationFailure($project, $e->getMessage(), $e->validation, $e); + } catch (Throwable $e) { + $this->error("Unexpected error publishing project {$project->name}: {$e->getMessage()}"); + $this->notifyPublicationFailure($project, $e->getMessage(), $project->validation?->fresh(), $e); + } + } + + return $publishedCount; + } + + private function notifyPublicationFailure(Project $project, string $reason, ?Validation $validation = null, ?Throwable $exception = null): void + { + $project->loadMissing('owner'); + $exceptionClass = $exception ? $exception::class : null; + + if ($project->owner) { + Notification::send( + $project->owner, + new EmbargoPublicationFailedNotification($project, $reason, $validation, $exceptionClass) + ); + } + + $admins = User::role(['super-admin']) + ->when($project->owner, fn ($query) => $query->whereKeyNot($project->owner->getKey())) + ->get(); + + Notification::send( + $admins, + new EmbargoPublicationFailedNotification($project, $reason, $validation, $exceptionClass, admin: true) + ); + } + + /** + * Clean up old embargo reminder records for projects that are no longer embargoed + */ + private function cleanupOldReminders(): void + { + $deletedCount = EmbargoReminder::whereHas('project', function ($query) { + $query->where('status', '!=', 'embargo') + ->orWhere('is_public', true) + ->orWhere('is_archived', true); + })->delete(); + + if ($deletedCount > 0) { + $this->info("Cleaned up {$deletedCount} old embargo reminder records."); + } + } +} diff --git a/app/Console/Commands/PublishReleasedProjects.php b/app/Console/Commands/PublishReleasedProjects.php deleted file mode 100644 index a91c06027..000000000 --- a/app/Console/Commands/PublishReleasedProjects.php +++ /dev/null @@ -1,52 +0,0 @@ -get(); - foreach ($projects as $project) { - $release_date = Carbon::parse($project->release_date); - if ($release_date->isPast()) { - if (! is_null($project->doi) && ! $project->is_archived) { - // echo($project->identifier); - // echo("\r\n"); - $publisher->publish($project); - Notification::send($project->owner, new DraftProcessedNotification($project)); - } - } - } - }); - } -} diff --git a/app/Console/Commands/QueueMetadataExtractionBagitGenerationJobs.php b/app/Console/Commands/QueueMetadataExtractionBagitGenerationJobs.php new file mode 100644 index 000000000..78dc66b62 --- /dev/null +++ b/app/Console/Commands/QueueMetadataExtractionBagitGenerationJobs.php @@ -0,0 +1,126 @@ +option('fresh')) { + if ($this->confirm('This will reset all BagIt status for all studies. Continue?', false)) { + Study::query()->update([ + 'metadata_bagit_generation_status' => null, + 'metadata_bagit_generation_logs' => null, + ]); + $this->info('✓ Cleared all existing BagIt status data'); + } else { + return self::SUCCESS; + } + } + + $query = Study::query() + ->where('has_nmrium', true) + ->where('is_public', true) + ->whereNotNull('download_url'); + + // Filter by specific study IDs if provided + if ($ids = $this->option('ids')) { + $studyIds = array_map('trim', explode(',', $ids)); + $query->whereIn('id', $studyIds); + $this->info('Processing '.count($studyIds).' specific study IDs...'); + } + + if ($this->option('retry-failed')) { + // Get failed study IDs from database + $failedStudies = Study::where('metadata_bagit_generation_status', 'failed')->get(); + + if ($failedStudies->isEmpty()) { + $this->warn('No failed jobs to retry.'); + + return self::SUCCESS; + } + + $query->whereIn('id', $failedStudies->pluck('id')); + $this->info('Retrying '.$failedStudies->count().' failed jobs...'); + + // Reset status to pending + Study::where('metadata_bagit_generation_status', 'failed') + ->whereIn('id', $failedStudies->pluck('id')) + ->update(['metadata_bagit_generation_status' => 'pending']); + } elseif (! $this->option('ids')) { + // Only exclude already processed studies when not targeting specific IDs + $query->where(function ($q) { + $q->whereNull('metadata_bagit_generation_status') + ->orWhereIn('metadata_bagit_generation_status', ['failed']); + }); + } + + if ($limit = $this->option('limit')) { + $query->limit((int) $limit); + } + + $studies = $query->get(); + + if ($studies->isEmpty()) { + $this->warn('No eligible studies found to process.'); + + return self::SUCCESS; + } + + $this->info("Found {$studies->count()} studies to process"); + + $bar = $this->output->createProgressBar($studies->count()); + $bar->setFormat('verbose'); + + $jobsDispatched = 0; + + foreach ($studies as $study) { + // Mark as pending with queued timestamp + $study->update([ + 'metadata_bagit_generation_status' => 'pending', + 'metadata_bagit_generation_logs' => [ + 'queued_at' => now()->toIso8601String(), + 'study_identifier' => str_replace('NMRXIV:', '', $study->identifier), + ], + ]); + + // Dispatch job to queue + ProcessMetadataExtractionBagitGenerationJob::dispatch($study->id); + + $jobsDispatched++; + $bar->advance(); + } + + $bar->finish(); + $this->newLine(2); + + $this->info("✓ Successfully dispatched {$jobsDispatched} jobs to the queue"); + + return self::SUCCESS; + } +} diff --git a/app/Console/Commands/RefreshNmriumInfo.php b/app/Console/Commands/RefreshNmriumInfo.php new file mode 100644 index 000000000..8fdf82a69 --- /dev/null +++ b/app/Console/Commands/RefreshNmriumInfo.php @@ -0,0 +1,350 @@ +error('NMRKIT_URL is not configured.'); + + return self::FAILURE; + } + + $query = Study::query()->whereHas('nmrium'); + if ($studyId = $this->option('study')) { + $query->whereKey($studyId); + } + + $studies = $query->get(); + if ($studies->isEmpty()) { + $this->info('No studies match.'); + + return self::SUCCESS; + } + + $controller = new StudyController; + $reflector = new ReflectionClass($controller); + $labelMethod = $reflector->getMethod('spectrumTypeLabel'); + $labelMethod->setAccessible(true); + + $touched = 0; + $skipped = 0; + foreach ($studies as $study) { + $nmrium = $study->nmrium; + if (! $nmrium) { + $skipped++; + + continue; + } + $payload = $nmrium->nmrium_info ?? []; + $spectra = $payload['data']['spectra'] ?? []; + if (! is_array($spectra) || empty($spectra)) { + $skipped++; + + continue; + } + + $jcampBackfilled = $this->backfillUnparsedJcampDatasets($study); + if (! $this->needsRefresh($spectra)) { + if ($jcampBackfilled > 0) { + $this->info(sprintf('study %d %s — info OK; classified %d JCAMP-only file(s)', $study->id, $study->name, $jcampBackfilled)); + $touched++; + } else { + $this->line(sprintf('study %d %s — info OK, skipping', $study->id, $study->name)); + $skipped++; + } + + continue; + } + + $url = $study->download_url; + if (! $url) { + $this->warn(sprintf('study %d %s — no download_url, skipping', $study->id, $study->name)); + $skipped++; + + continue; + } + + $this->line(sprintf('study %d %s — fetching from NMRKit', $study->id, $study->name)); + + try { + $response = Http::timeout(120)->post($parserUrl, [ + 'url' => $url, + 'capture_snapshot' => false, + ]); + } catch (\Throwable $e) { + $this->error(sprintf(' HTTP error: %s', $e->getMessage())); + Log::warning('refresh-nmrium-info HTTP failure', [ + 'study_id' => $study->id, + 'error' => $e->getMessage(), + ]); + $skipped++; + + continue; + } + + if (! $response->ok()) { + $this->error(sprintf(' NMRKit returned HTTP %d', $response->status())); + $skipped++; + + continue; + } + + $body = $response->json(); + $parsedSpectra = $body['nmriumState']['data']['spectra'] ?? $body['data']['spectra'] ?? []; + if (! is_array($parsedSpectra) || empty($parsedSpectra)) { + $this->warn(' NMRKit response had no spectra'); + $skipped++; + + continue; + } + + $infoIndex = $this->buildInfoIndex($parsedSpectra); + if (empty($infoIndex)) { + $this->warn(' Could not extract any info blocks from NMRKit response'); + $skipped++; + + continue; + } + + $updatedSpectra = []; + $merged = 0; + foreach ($spectra as $spec) { + $key = $this->spectrumKey($spec); + if ($key !== null && isset($infoIndex[$key])) { + $info = $infoIndex[$key]; + if (is_array($info) && ! empty($info)) { + $spec['info'] = $info; + $merged++; + } + } + $updatedSpectra[] = $spec; + } + + if ($merged === 0) { + $this->warn(' No spectra matched between stored payload and NMRKit response'); + $skipped++; + + continue; + } + + $this->info(sprintf(' matched %d/%d spectra', $merged, count($spectra))); + + if ($this->option('dry')) { + continue; + } + + $payload['data']['spectra'] = $updatedSpectra; + $nmrium->nmrium_info = $payload; + $nmrium->save(); + + $this->refreshDatasetTypes($study, $updatedSpectra, $controller, $labelMethod); + $this->backfillUnparsedJcampDatasets($study); + $touched++; + } + + $this->newLine(); + $this->info(sprintf('Done. Updated %d studies, skipped %d.', $touched, $skipped)); + + return self::SUCCESS; + } + + /** + * Build a lookup keyed by the first selector file path so we can match + * stored spectra to NMRKit's response. NMRKit's payload uses either + * `selector` or `sourceSelector`; we accept both. + * + * @param array> $parsedSpectra + * @return array> + */ + protected function buildInfoIndex(array $parsedSpectra): array + { + $index = []; + foreach ($parsedSpectra as $spec) { + $key = $this->spectrumKey($spec); + if ($key === null) { + continue; + } + $info = $spec['info'] ?? null; + if (is_array($info) && ! empty($info)) { + $index[$key] = $info; + } + } + + return $index; + } + + /** + * Reduce a spectrum payload to a stable key (the basename + parent expno + * folder of its first selector file). Identifies the same NMR experiment + * across the saved snapshot and a fresh NMRKit re-parse. + * + * @param array $spectrum + */ + protected function spectrumKey(array $spectrum): ?string + { + $selector = $spectrum['sourceSelector'] ?? $spectrum['selector'] ?? []; + $files = $selector['files'] ?? []; + if (! is_array($files) || empty($files)) { + return null; + } + foreach ($files as $file) { + if (! is_string($file) || $file === '') { + continue; + } + $parts = explode('/', $file); + $base = array_pop($parts); + $parent = $parts ? array_pop($parts) : ''; + // For Bruker `pdata//` paths, climb to the expno. + if ($parent === '1' && ! empty($parts) && end($parts) === 'pdata') { + array_pop($parts); + $parent = $parts ? array_pop($parts) : ''; + } + + return $parent.'/'.$base; + } + + return null; + } + + /** + * Heuristically decide whether at least one spectrum in the stored + * payload is missing real metadata. We treat `info` as "needs refresh" + * when it is empty, missing the `experiment` key, or has been corrupted + * with raw `{im, re}` payloads from the legacy SpectraEditor bug. + * + * @param array> $spectra + */ + protected function needsRefresh(array $spectra): bool + { + foreach ($spectra as $spec) { + $info = $spec['info'] ?? null; + if (! is_array($info) || empty($info)) { + return true; + } + if (isset($info['im']) || isset($info['re'])) { + return true; + } + if (! array_key_exists('experiment', $info) && ! array_key_exists('nucleus', $info)) { + return true; + } + } + + return false; + } + + /** + * Re-derive `dataset.type` for every dataset of the study using the same + * spectrum-to-dataset matching the controller does on save. + */ + protected function refreshDatasetTypes(Study $study, array $spectraAll, StudyController $controller, \ReflectionMethod $labelMethod): void + { + $studyFSObject = $study->fsObject; + if (! $studyFSObject) { + return; + } + $draft = $study->draft; + $isChemotion = $draft && $draft->eln === 'chemotion'; + + foreach ($study->datasets as $dataset) { + $dsFSO = $dataset->fsObject; + if (! $dsFSO) { + continue; + } + $parentName = $isChemotion ? optional($dsFSO->parent)->name : null; + if ($isChemotion && $parentName === null) { + continue; + } + $path = $isChemotion + ? '/'.$studyFSObject->name.'/'.$parentName.'/'.$dsFSO->name + : '/'.$studyFSObject->name.'/'.$dsFSO->name; + $fType = $studyFSObject->type; + + $types = []; + foreach ($spectraAll as $sp) { + $sel = $sp['sourceSelector'] ?? $sp['selector'] ?? []; + $files = $sel['files'] ?? []; + $match = false; + foreach ($files as $f) { + if (! is_string($f)) { + continue; + } + if (str_contains($f, $fType === 'file' ? $path : $path.'/')) { + $match = true; + + break; + } + } + if (! $match) { + continue; + } + $label = $labelMethod->invoke($controller, $sp); + if ($label !== null) { + $types[] = $label; + } + } + $unique = array_values(array_unique($types)); + $new = count($unique) === 1 + ? $unique[0] + : (count($unique) > 1 ? implode(' / ', $unique) : null); + if ($new !== null && $new !== $dataset->type) { + $dataset->type = $new; + $dataset->save(); + } + } + } + + /** + * Classify any `.jdx` / `.dx` / `.jcamp` datasets that NMRium dropped from + * its parsed spectra list — typically MestReNova LINK files that carry a + * peak-assignment table or chemical structure but no `XYDATA` block. We + * read the file's own JCAMP headers (nucleus + DATA TYPE) so the upload + * sidebar shows e.g. `1H NMR - Peak Assignments` instead of an empty card. + * + * Returns the number of datasets whose `dataset.type` was filled in. + * Honours `--dry` by walking the same flow but skipping persistence. + */ + protected function backfillUnparsedJcampDatasets(Study $study): int + { + if ($this->option('dry')) { + return 0; + } + + $updates = (new JcampDatasetClassifier)->classifyStudy($study); + foreach ($updates as $dsId => $label) { + $this->line(sprintf(' classified dataset %d → %s', $dsId, $label)); + } + + return count($updates); + } +} diff --git a/app/Console/Commands/SanitizeMolecules.php b/app/Console/Commands/SanitizeMolecules.php index ad6313f94..485ac8fa6 100644 --- a/app/Console/Commands/SanitizeMolecules.php +++ b/app/Console/Commands/SanitizeMolecules.php @@ -3,8 +3,13 @@ namespace App\Console\Commands; use App\Models\Molecule; +use App\Services\CAS\CASService; use Illuminate\Console\Command; +use Illuminate\Http\Client\ConnectionException; +use Illuminate\Http\Client\RequestException; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Log; class SanitizeMolecules extends Command { @@ -13,118 +18,383 @@ class SanitizeMolecules extends Command * * @var string */ - protected $signature = 'nmrxiv:molecules-clean'; + protected $signature = 'nmrxiv:molecules-clean + {--limit= : Limit the number of molecules to process} + {--force : Skip confirmation prompt}'; /** * The console command description. * * @var string */ - protected $description = 'Sanitize molecules'; + protected $description = 'Sanitize and enrich molecule data with PubChem properties, CAS numbers, and standardized structures'; + + /** + * Statistics for tracking progress + */ + private int $processedCount = 0; + + private int $updatedCount = 0; + + private int $errorCount = 0; + + /** + * API rate limiting delay in microseconds (500ms) + */ + private const API_DELAY_MICROSECONDS = 500000; + + public function __construct( + private CASService $casService + ) { + parent::__construct(); + } /** * Execute the console command. */ - public function handle(): void + public function handle(): int { - $molecules = Molecule::all(); - - foreach ($molecules as $molecule) { - echo $molecule->id; - echo "\r\n"; - $inchi = $molecule->standard_inchi; - if ($inchi) { - $data = $this->fetchPubChemIUPACProperties($inchi); - $molecule->synonyms = $data['synonyms']; - $molecule->iupac_name = (array_key_exists('IUPACName', $data['properties']) ? $data['properties']['IUPACName'] : $molecule->iupac_name); - $molecule->molecular_formula = (array_key_exists('MolecularFormula', $data['properties']) ? $data['properties']['MolecularFormula'] : $molecule->molecular_formula); - $molecule->molecular_weight = (array_key_exists('MolecularWeight', $data['properties']) ? $data['properties']['MolecularWeight'] : $molecule->molecular_weight); - $molecule->Save(); - } - if (! $molecule->canonical_smiles) { - echo $molecule->id; - echo "\r\n"; - $molecule->sdf = ' - '.$molecule->sdf; - $standardisedMOL = $this->standardizeMolecule($molecule->sdf); - $molecule->canonical_smiles = array_key_exists('canonical_smiles', $standardisedMOL) ? $standardisedMOL['canonical_smiles'] : null; - $molecule->standard_inchi = array_key_exists('inchi', $standardisedMOL) ? $standardisedMOL['inchi'] : null; - $molecule->inchi_key = array_key_exists('canonicalinchikey_smiles', $standardisedMOL) ? $standardisedMOL['inchikey'] : null; - $molecule->save(); + $totalMolecules = $this->option('limit') + ? min((int) $this->option('limit'), Molecule::count()) + : Molecule::count(); + + $this->info("Processing {$totalMolecules} molecules..."); + + if ($totalMolecules === 0) { + $this->info('No molecules found to process.'); + + return self::SUCCESS; + } + + if (! $this->option('force') && ! $this->confirm('Do you want to continue?', true)) { + $this->info('Operation cancelled.'); + + return self::SUCCESS; + } + + $progressBar = $this->output->createProgressBar($totalMolecules); + $progressBar->start(); + + $query = Molecule::query(); + + if ($this->option('limit')) { + $query->limit((int) $this->option('limit')); + } + + $query->chunk(100, function ($molecules) use ($progressBar) { + foreach ($molecules as $molecule) { + $this->processMolecule($molecule); + $progressBar->advance(); } - if ($molecule->canonical_smiles) { - $cas = $this->fetchCAS($molecule->canonical_smiles); - $molecule->cas = $cas; + }); + + $progressBar->finish(); + $this->newLine(2); + $this->displayStatistics(); + + return self::SUCCESS; + } + + /** + * Process a single molecule + */ + protected function processMolecule(Molecule $molecule): void + { + try { + DB::beginTransaction(); + + $updated = false; + + // Fetch PubChem properties if standard InChI is available + if ($molecule->standard_inchi) { + $updated = $this->enrichWithPubChemData($molecule) || $updated; + } + + // Standardize molecule structure if canonical SMILES is missing + if (! $molecule->canonical_smiles && $molecule->sdf) { + $updated = $this->standardizeMoleculeStructure($molecule) || $updated; + } + + // Fetch CAS number if canonical SMILES is available + if ($molecule->canonical_smiles && ! $molecule->cas) { + $updated = $this->enrichWithCASNumber($molecule) || $updated; + } + + // Only save if something was updated + if ($updated) { $molecule->save(); + $this->updatedCount++; } + + $this->processedCount++; + + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + $this->errorCount++; + + Log::error('Failed to process molecule', [ + 'molecule_id' => $molecule->id, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + $this->warn("Failed to process molecule ID {$molecule->id}: ".$e->getMessage()); } + + // Rate limiting delay + usleep(self::API_DELAY_MICROSECONDS); } - protected function fetchPubChemIUPACProperties($inchi) + /** + * Enrich molecule with PubChem data + */ + protected function enrichWithPubChemData(Molecule $molecule): bool { - $pubchemBase = rtrim(config('services.pubchem.base_url'), '/'); - $pubchemPug = trim(config('services.pubchem.pug_rest_path'), '/'); - $cid_response = Http::get($pubchemBase.'/'.$pubchemPug.'/compound/inchi/cids/JSON', [ - 'inchi' => $inchi, - ]); - $cid_data = $cid_response->json(); - $cid = null; - if (array_key_exists('IdentifierList', $cid_data)) { - $cids = $cid_data['IdentifierList']['CID']; - $cid = $cids[0]; - } - $synonyms = ''; - $properties = []; - if ($cid) { - $synonyms_response = Http::get($pubchemBase.'/'.$pubchemPug.'/compound/cid/'.$cid.'/Synonyms/JSON'); - $synonyms_data = $synonyms_response->json(); - if (! array_key_exists('Fault', $synonyms_data)) { - $synonyms = implode(',', $synonyms_data['InformationList']['Information'][0]['Synonym']); + try { + $data = $this->fetchPubChemIUPACProperties($molecule->standard_inchi); + + if (empty($data['synonyms']) && empty($data['properties'])) { + return false; + } + + $updated = false; + + if (! empty($data['synonyms']) && $molecule->synonyms !== $data['synonyms']) { + $molecule->synonyms = $data['synonyms']; + $updated = true; + } + + if (! empty($data['properties']['IUPACName']) && ! $molecule->iupac_name) { + $molecule->iupac_name = $data['properties']['IUPACName']; + $updated = true; + } + + if (! empty($data['properties']['MolecularFormula']) && ! $molecule->molecular_formula) { + $molecule->molecular_formula = $data['properties']['MolecularFormula']; + $updated = true; } - $properties_response = Http::get($pubchemBase.'/'.$pubchemPug.'/compound/cid/'.$cid.'/property/IUPACName,MolecularWeight,MolecularFormula/JSON'); - $properties_data = $properties_response->json(); - if (! array_key_exists('Fault', $properties_data)) { - $properties = $properties_data['PropertyTable']['Properties'][0]; + if (! empty($data['properties']['MolecularWeight']) && ! $molecule->molecular_weight) { + $molecule->molecular_weight = (float) $data['properties']['MolecularWeight']; + $updated = true; } + + return $updated; + } catch (\Exception $e) { + Log::warning('Failed to fetch PubChem data', [ + 'molecule_id' => $molecule->id, + 'inchi' => $molecule->standard_inchi, + 'error' => $e->getMessage(), + ]); + + return false; } + } - return [ - 'synonyms' => $synonyms, - 'properties' => $properties, - ]; + /** + * Standardize molecule structure + */ + protected function standardizeMoleculeStructure(Molecule $molecule): bool + { + try { + $standardisedMOL = $this->standardizeMolecule($molecule->sdf); + + if (empty($standardisedMOL)) { + return false; + } + + $updated = false; + + if (! empty($standardisedMOL['canonical_smiles'])) { + $molecule->canonical_smiles = $standardisedMOL['canonical_smiles']; + $updated = true; + } + + if (! empty($standardisedMOL['inchi'])) { + $molecule->standard_inchi = $standardisedMOL['inchi']; + $updated = true; + } + + if (! empty($standardisedMOL['inchikey'])) { + $molecule->inchi_key = $standardisedMOL['inchikey']; + $updated = true; + } + + return $updated; + } catch (\Exception $e) { + Log::warning('Failed to standardize molecule', [ + 'molecule_id' => $molecule->id, + 'error' => $e->getMessage(), + ]); + + return false; + } } - protected function fetchCAS($smiles) + /** + * Enrich molecule with CAS number + */ + protected function enrichWithCASNumber(Molecule $molecule): bool { try { - $ccBase = rtrim(config('services.common_chemistry.base_url'), '/'); - $ccApi = trim(config('services.common_chemistry.api_path'), '/'); - $response = Http::get($ccBase.'/'.$ccApi.'/search', [ - 'q' => $smiles, + $cas = $this->fetchCAS($molecule->canonical_smiles); + + if ($cas) { + $molecule->cas = $cas; + + return true; + } + + return false; + } catch (\Exception $e) { + Log::warning('Failed to fetch CAS number', [ + 'molecule_id' => $molecule->id, + 'smiles' => $molecule->canonical_smiles, + 'error' => $e->getMessage(), ]); - if (! $response->failed()) { - $data = $response->json(); - if ($data['count'] > 0) { - $cas = $data['results'][0]['rn']; - return $cas; + return false; + } + } + + /** + * Fetch PubChem IUPAC properties + * + * @return array{synonyms: string, properties: array} + */ + protected function fetchPubChemIUPACProperties(string $inchi): array + { + $pubchemBase = rtrim(config('services.pubchem.base_url'), '/'); + $pubchemPug = trim(config('services.pubchem.pug_rest_path'), '/'); + + try { + // Fetch CID from InChI with retry + $cidResponse = Http::retry(3, 1000) + ->timeout(30) + ->get($pubchemBase.'/'.$pubchemPug.'/compound/inchi/cids/JSON', [ + 'inchi' => $inchi, + ]); + + $cidResponse->throw(); + $cidData = $cidResponse->json(); + + if (! isset($cidData['IdentifierList']['CID'][0])) { + return ['synonyms' => '', 'properties' => []]; + } + + $cid = $cidData['IdentifierList']['CID'][0]; + + // Fetch synonyms + $synonyms = ''; + try { + $synonymsResponse = Http::retry(3, 1000) + ->timeout(30) + ->get($pubchemBase.'/'.$pubchemPug.'/compound/cid/'.$cid.'/Synonyms/JSON'); + + $synonymsResponse->throw(); + $synonymsData = $synonymsResponse->json(); + + if (isset($synonymsData['InformationList']['Information'][0]['Synonym'])) { + $synonyms = implode(',', $synonymsData['InformationList']['Information'][0]['Synonym']); } + } catch (RequestException $e) { + Log::debug('Failed to fetch PubChem synonyms', ['cid' => $cid, 'error' => $e->getMessage()]); } - } catch (\Illuminate\Http\Client\ConnectionException $e) { - echo 'timed out: '.$smiles; + + // Fetch properties + $properties = []; + try { + $propertiesResponse = Http::retry(3, 1000) + ->timeout(30) + ->get($pubchemBase.'/'.$pubchemPug.'/compound/cid/'.$cid.'/property/IUPACName,MolecularWeight,MolecularFormula/JSON'); + + $propertiesResponse->throw(); + $propertiesData = $propertiesResponse->json(); + + if (isset($propertiesData['PropertyTable']['Properties'][0])) { + $properties = $propertiesData['PropertyTable']['Properties'][0]; + } + } catch (RequestException $e) { + Log::debug('Failed to fetch PubChem properties', ['cid' => $cid, 'error' => $e->getMessage()]); + } + + return [ + 'synonyms' => $synonyms, + 'properties' => $properties, + ]; + } catch (RequestException $e) { + throw new \RuntimeException("Failed to fetch PubChem data for InChI: {$e->getMessage()}", 0, $e); + } + } + + /** + * Fetch CAS number from SMILES + */ + protected function fetchCAS(string $smiles): ?string + { + if (! config('services.cas.api_token')) { + return null; + } + + try { + return $this->casService->searchCASBySmiles($smiles); + } catch (\Exception $e) { + Log::debug('Failed to fetch CAS', [ + 'smiles' => $smiles, + 'error' => $e->getMessage(), + ]); + + return null; } } - protected function standardizeMolecule($mol) + /** + * Standardize molecule structure + * + * @return array|null + */ + protected function standardizeMolecule(string $mol): ?array { try { $stdUrl = config('services.chemistry_standardize.url'); - $response = Http::post($stdUrl, $mol); + + if (! $stdUrl) { + return null; + } + + $response = Http::retry(3, 1000) + ->timeout(30) + ->post($stdUrl, $mol); + + $response->throw(); return $response->json(); - } catch (\Illuminate\Http\Client\ConnectionException $e) { - echo 'timed out: '.$mol; + } catch (ConnectionException $e) { + Log::warning('Chemistry standardize API connection failed', ['error' => $e->getMessage()]); + + return null; + } catch (RequestException $e) { + Log::warning('Chemistry standardize API request failed', ['error' => $e->getMessage()]); + + return null; } } + + /** + * Display processing statistics + */ + protected function displayStatistics(): void + { + $this->info('Molecule sanitization completed!'); + $this->newLine(); + $this->table( + ['Metric', 'Count'], + [ + ['Processed', $this->processedCount], + ['Updated', $this->updatedCount], + ['Errors', $this->errorCount], + ] + ); + } } diff --git a/app/Console/Commands/SanitizeProjects.php b/app/Console/Commands/SanitizeProjects.php index 1a4370d4e..f07c80068 100644 --- a/app/Console/Commands/SanitizeProjects.php +++ b/app/Console/Commands/SanitizeProjects.php @@ -93,7 +93,7 @@ public function cleanProjects() $user_id = $project->owner->id; $team_id = $project->team_id; $id = Str::uuid(); - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $path = preg_replace( '~//+~', '/', @@ -169,7 +169,7 @@ public function cleanDrafts() $user_id = $project->owner->id; $team_id = $project->team_id; $id = Str::uuid(); - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $path = preg_replace( '~//+~', '/', @@ -362,7 +362,7 @@ public function updateFilesStatus() } if ($fsObject->path && $fsObject->type == 'file') { - $exists = Storage::disk(env('FILESYSTEM_DRIVER'))->exists($fsObject->path); + $exists = Storage::disk(config('filesystems.default'))->exists($fsObject->path); if (! $exists) { $fsObject->status = 'missing'; } else { @@ -382,7 +382,7 @@ public function updateFilePaths() if (! $fsObject->path) { $project = $fsObject->project; if ($project) { - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $path = preg_replace( '~//+~', '/', @@ -431,7 +431,7 @@ public function moveFolder($fsObject, $project, $draft) if ($fsObjectChild->type == 'file') { $newFilePath = '/'.$draft->path.$fsObjectChild->relative_url; if ($fsObjectChild->path && $newFilePath && $fsObjectChild->path != $newFilePath) { - Storage::disk(env('FILESYSTEM_DRIVER'))->move($fsObjectChild->path, $newFilePath); + Storage::disk(config('filesystems.default'))->move($fsObjectChild->path, $newFilePath); $fsObjectChild->path = $newFilePath; $fsObjectChild->draft_id = $draft->id; $fsObjectChild->save(); diff --git a/app/Console/Commands/UnpublishProjects.php b/app/Console/Commands/UnpublishProjects.php index 1a653c5ef..88bce7329 100644 --- a/app/Console/Commands/UnpublishProjects.php +++ b/app/Console/Commands/UnpublishProjects.php @@ -41,7 +41,7 @@ public function handle(UnPublishProject $unpublisher): int $user_id = $project->owner->id; $team_id = $project->team_id; $id = Str::uuid(); - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $path = preg_replace( '~//+~', '/', @@ -102,7 +102,7 @@ public function moveFolder($fsObject, $project, $draft) if ($fsObjectChild->type == 'file') { $newFilePath = '/'.$draft->path.$fsObjectChild->relative_url; if ($fsObjectChild->path && $newFilePath && $fsObjectChild->path != $newFilePath) { - Storage::disk(env('FILESYSTEM_DRIVER'))->move($fsObjectChild->path, $newFilePath); + Storage::disk(config('filesystems.default'))->move($fsObjectChild->path, $newFilePath); $fsObjectChild->path = $newFilePath; $fsObjectChild->draft_id = $draft->id; $fsObjectChild->save(); diff --git a/app/Console/Commands/UpdateDOI.php b/app/Console/Commands/UpdateDOI.php index 806648b43..07242cc61 100644 --- a/app/Console/Commands/UpdateDOI.php +++ b/app/Console/Commands/UpdateDOI.php @@ -27,7 +27,7 @@ class UpdateDOI extends Command /** * The DOI service instance. * - * @var \App\Services\DOI\DOIService + * @var DOIService */ protected $doiService; diff --git a/app/Console/Commands/UpdateProjectStatuses.php b/app/Console/Commands/UpdateProjectStatuses.php new file mode 100644 index 000000000..d00badb7d --- /dev/null +++ b/app/Console/Commands/UpdateProjectStatuses.php @@ -0,0 +1,126 @@ +option('dry-run'); + + if ($isDryRun) { + $this->info('🔍 DRY RUN MODE - No changes will be made'); + } else { + $this->info('🚀 LIVE MODE - Project statuses will be updated'); + } + + $this->newLine(); + + return DB::transaction(function () use ($isDryRun) { + $now = Carbon::now(); + $updatedCount = 0; + + // Get all projects with their current data + $projects = Project::all(); + $this->info("📊 Found {$projects->count()} total projects to analyze"); + + // Track updates by status type + $statusCounts = [ + 'deleted' => 0, + 'archived' => 0, + 'embargo' => 0, + 'published' => 0, + 'draft' => 0, + ]; + + foreach ($projects as $project) { + $newStatus = $this->determineProjectStatus($project, $now); + $oldStatus = $project->status; + + if ($newStatus !== $oldStatus) { + $this->info("📝 Project {$project->id} ({$project->name}): '{$oldStatus}' → '{$newStatus}'"); + + if (! $isDryRun) { + $project->update(['status' => $newStatus]); + } + + $updatedCount++; + $statusCounts[$newStatus]++; + } else { + $this->line("✓ Project {$project->id} already has correct status: '{$newStatus}'"); + } + } + + $this->newLine(); + $this->info('📈 Status Distribution:'); + foreach ($statusCounts as $status => $count) { + if ($count > 0) { + $this->line(" • {$status}: {$count} projects"); + } + } + + $this->newLine(); + if ($isDryRun) { + $this->info("🔍 DRY RUN COMPLETE: {$updatedCount} projects would be updated"); + $this->info('💡 Run without --dry-run to apply changes'); + } else { + $this->info("✅ SUCCESS: Updated {$updatedCount} project statuses"); + } + + return 0; + }); + } + + /** + * Determine the correct status for a project based on its flags + */ + private function determineProjectStatus(Project $project, Carbon $now): string + { + // Priority order based on business logic: + + // 1. If deleted, status is 'deleted' + if ($project->is_deleted) { + return 'deleted'; + } + + // 2. If archived, status is 'archived' + if ($project->is_archived) { + return 'archived'; + } + + // 3. If public, status is 'published' + if ($project->is_public) { + return 'published'; + } + + // 4. If has future release_date, status is 'embargo' + if ($project->release_date && Carbon::parse($project->release_date)->isAfter($now)) { + return 'embargo'; + } + + // 5. Default to 'draft' for everything else + return 'draft'; + } +} diff --git a/app/Console/Commands/VerifyDatasetNmriumInfoCommand.php b/app/Console/Commands/VerifyDatasetNmriumInfoCommand.php new file mode 100644 index 000000000..7dc76d8fb --- /dev/null +++ b/app/Console/Commands/VerifyDatasetNmriumInfoCommand.php @@ -0,0 +1,172 @@ +option('chunk')); + $limit = $this->option('limit'); + $limit = $limit !== null && $limit !== '' ? max(1, (int) $limit) : null; + $maxExceptionLines = max(0, (int) $this->option('show-exceptions')); + + $total = 0; + $exceptions = 0; + $infoNonNull = 0; + $infoNull = 0; + $solventWhenInfo = 0; + $nullNoNmrium = 0; + $nullDatasetNmriumOnly = 0; + $nullStudyNmriumOnly = 0; + $nullBothNmrium = 0; + + $exceptionSamples = []; + + $query = Dataset::query() + ->select(['id', 'study_id', 'fs_id']) + ->with(['nmrium', 'study.nmrium', 'fsObject:id,name']) + ->orderBy('id'); + + $query->chunkById($chunkSize, function ($datasets) use ( + &$total, + &$exceptions, + &$infoNonNull, + &$infoNull, + &$solventWhenInfo, + &$nullNoNmrium, + &$nullDatasetNmriumOnly, + &$nullStudyNmriumOnly, + &$nullBothNmrium, + &$exceptionSamples, + $limit, + $maxExceptionLines + ) { + foreach ($datasets as $dataset) { + if ($limit !== null && $total >= $limit) { + return false; + } + + $total++; + + $hasDatasetNmrium = $dataset->nmrium !== null; + $hasStudyNmrium = $dataset->study?->nmrium !== null; + + try { + $info = BioschemasHelper::getNMRiumInfo($dataset); + } catch (Throwable $e) { + $exceptions++; + if (count($exceptionSamples) < $maxExceptionLines) { + $exceptionSamples[] = sprintf( + 'dataset_id=%d study_id=%s error=%s', + $dataset->id, + $dataset->study_id ?? 'null', + $e->getMessage() + ); + } + + continue; + } + + if ($info !== null) { + $infoNonNull++; + if ($this->solventFromInfo($info) !== null) { + $solventWhenInfo++; + } + + continue; + } + + $infoNull++; + + if (! $hasDatasetNmrium && ! $hasStudyNmrium) { + $nullNoNmrium++; + } elseif ($hasDatasetNmrium && ! $hasStudyNmrium) { + $nullDatasetNmriumOnly++; + } elseif (! $hasDatasetNmrium && $hasStudyNmrium) { + $nullStudyNmriumOnly++; + } else { + $nullBothNmrium++; + } + } + }); + + $this->table( + ['Metric', 'Count'], + [ + ['Total datasets scanned', (string) $total], + ['getNMRiumInfo returned non-null', (string) $infoNonNull], + ['… with non-empty solvent in info', (string) $solventWhenInfo], + ['getNMRiumInfo returned null', (string) $infoNull], + [' … no NMRium on dataset or study', (string) $nullNoNmrium], + [' … dataset NMRium only (null info)', (string) $nullDatasetNmriumOnly], + [' … study NMRium only, no dataset row (null info)', (string) $nullStudyNmriumOnly], + [' … both dataset + study NMRium (null info)', (string) $nullBothNmrium], + ['Exceptions thrown by getNMRiumInfo', (string) $exceptions], + ] + ); + + if ($exceptions > 0 && $exceptionSamples !== []) { + $this->warn('Sample exceptions (trim with --show-exceptions=0 to hide):'); + foreach ($exceptionSamples as $line) { + $this->line(' '.$line); + } + } + + if ($total === 0) { + $this->info('No datasets in the database.'); + + return self::SUCCESS; + } + + $pctInfo = round(100 * $infoNonNull / $total, 1); + $this->info("Non-null info rate: {$pctInfo}% ({$infoNonNull}/{$total})."); + + if ($infoNonNull > 0) { + $pctSolvent = round(100 * $solventWhenInfo / $infoNonNull, 1); + $this->info("Of datasets with info, {$pctSolvent}% have solvent set ({$solventWhenInfo}/{$infoNonNull})."); + } + + return self::SUCCESS; + } + + /** + * @param mixed $info + */ + private function solventFromInfo($info): ?string + { + if ($info === null) { + return null; + } + if (is_array($info)) { + $s = $info['solvent'] ?? null; + + return ($s !== null && $s !== '') ? (string) $s : null; + } + if (is_object($info) && property_exists($info, 'solvent')) { + $s = $info->solvent; + + return ($s !== null && $s !== '') ? (string) $s : null; + } + + return null; + } +} diff --git a/app/Events/AddingProjectMember.php b/app/Events/AddingProjectMember.php index 580791b07..67b6214bc 100644 --- a/app/Events/AddingProjectMember.php +++ b/app/Events/AddingProjectMember.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -11,20 +12,29 @@ class AddingProjectMember { use Dispatchable, InteractsWithSockets, SerializesModels; + /** + * The project instance. + */ + public $project; + + /** + * The user instance. + */ + public $user; + /** * Create a new event instance. - * - * @return void */ - public function __construct() + public function __construct($project, $user) { - // + $this->project = $project; + $this->user = $user; } /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/AddingStudyMember.php b/app/Events/AddingStudyMember.php index 52299bbb9..82479f0e1 100644 --- a/app/Events/AddingStudyMember.php +++ b/app/Events/AddingStudyMember.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/DraftCompleted.php b/app/Events/DraftCompleted.php index 9611c0545..ef6f6ca9f 100644 --- a/app/Events/DraftCompleted.php +++ b/app/Events/DraftCompleted.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/DraftProcessed.php b/app/Events/DraftProcessed.php index 8d37cc89d..132f96fd8 100644 --- a/app/Events/DraftProcessed.php +++ b/app/Events/DraftProcessed.php @@ -2,9 +2,11 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Support\Facades\Log; class DraftProcessed implements ShouldBroadcastNow { @@ -23,12 +25,18 @@ public function __construct($project, $sendTo) { $this->project = $project; $this->sendTo = $sendTo; + + Log::info('embargo_publish_trace', [ + 'stage' => 'draft_processed_event_dispatched', + 'project_id' => $project->id, + 'send_to_count' => is_countable($sendTo) ? count($sendTo) : 0, + ]); } /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/InvitingTeamMember.php b/app/Events/InvitingTeamMember.php index b1962dee9..8ff8dcf95 100644 --- a/app/Events/InvitingTeamMember.php +++ b/app/Events/InvitingTeamMember.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/ProjectArchival.php b/app/Events/ProjectArchival.php index 25805ef89..ed9eb52f1 100644 --- a/app/Events/ProjectArchival.php +++ b/app/Events/ProjectArchival.php @@ -2,9 +2,11 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Support\Facades\Log; class ProjectArchival implements ShouldBroadcastNow { @@ -21,12 +23,18 @@ public function __construct($project, $sendTo) { $this->project = $project; $this->sendTo = $sendTo; + + Log::info('embargo_publish_trace', [ + 'stage' => 'project_archival_event_dispatched', + 'project_id' => $project->id, + 'send_to_count' => is_countable($sendTo) ? count($sendTo) : 0, + ]); } /** * Get the channels the event should broadcast on. * - * @return array + * @return array */ public function broadcastOn(): array { diff --git a/app/Events/ProjectDeletion.php b/app/Events/ProjectDeletion.php index 2ed4b9010..100e780b8 100644 --- a/app/Events/ProjectDeletion.php +++ b/app/Events/ProjectDeletion.php @@ -2,9 +2,11 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Support\Facades\Log; class ProjectDeletion implements ShouldBroadcastNow { @@ -21,12 +23,18 @@ public function __construct($project, $sendTo) { $this->project = $project; $this->sendTo = $sendTo; + + Log::info('embargo_publish_trace', [ + 'stage' => 'project_deletion_event_dispatched', + 'project_id' => $project->id, + 'send_to_count' => is_countable($sendTo) ? count($sendTo) : 0, + ]); } /** * Get the channels the event should broadcast on. * - * @return array + * @return array */ public function broadcastOn(): array { diff --git a/app/Events/ProjectInvite.php b/app/Events/ProjectInvite.php index 1097d3be3..22e8eba70 100644 --- a/app/Events/ProjectInvite.php +++ b/app/Events/ProjectInvite.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Foundation\Events\Dispatchable; @@ -26,7 +27,7 @@ public function __construct($invitedUser, $invitation) /** * Get the channels the event should broadcast on. * - * @return array + * @return array */ public function broadcastOn(): array { diff --git a/app/Events/ProjectMemberAdded.php b/app/Events/ProjectMemberAdded.php index 5958d717c..026318aa0 100644 --- a/app/Events/ProjectMemberAdded.php +++ b/app/Events/ProjectMemberAdded.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -11,20 +12,29 @@ class ProjectMemberAdded { use Dispatchable, InteractsWithSockets, SerializesModels; + /** + * The project instance. + */ + public $project; + + /** + * The user instance. + */ + public $user; + /** * Create a new event instance. - * - * @return void */ - public function __construct() + public function __construct($project, $user) { - // + $this->project = $project; + $this->user = $user; } /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/ProjectMemberRemoved.php b/app/Events/ProjectMemberRemoved.php index 44405cf72..f683af1e9 100644 --- a/app/Events/ProjectMemberRemoved.php +++ b/app/Events/ProjectMemberRemoved.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/ProjectMemberUpdated.php b/app/Events/ProjectMemberUpdated.php index 2fcf4c1d4..1cd4074bf 100644 --- a/app/Events/ProjectMemberUpdated.php +++ b/app/Events/ProjectMemberUpdated.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/StudyInvite.php b/app/Events/StudyInvite.php index 5f386b859..26daa12ad 100644 --- a/app/Events/StudyInvite.php +++ b/app/Events/StudyInvite.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Foundation\Events\Dispatchable; @@ -26,7 +27,7 @@ public function __construct($invitedUser, $invitation) /** * Get the channels the event should broadcast on. * - * @return array + * @return array */ public function broadcastOn(): array { diff --git a/app/Events/StudyMemberAdded.php b/app/Events/StudyMemberAdded.php index 95885ce17..e94b8613b 100644 --- a/app/Events/StudyMemberAdded.php +++ b/app/Events/StudyMemberAdded.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/StudyMemberRemoved.php b/app/Events/StudyMemberRemoved.php index a7630bdf7..a51259bb2 100644 --- a/app/Events/StudyMemberRemoved.php +++ b/app/Events/StudyMemberRemoved.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/StudyMemberUpdated.php b/app/Events/StudyMemberUpdated.php index b7bb1ebe8..e785b2491 100644 --- a/app/Events/StudyMemberUpdated.php +++ b/app/Events/StudyMemberUpdated.php @@ -2,6 +2,7 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -24,7 +25,7 @@ public function __construct() /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Events/StudyPublish.php b/app/Events/StudyPublish.php index a751cd325..af23633f6 100644 --- a/app/Events/StudyPublish.php +++ b/app/Events/StudyPublish.php @@ -2,9 +2,11 @@ namespace App\Events; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Support\Facades\Log; class StudyPublish implements ShouldBroadcastNow { @@ -23,12 +25,18 @@ public function __construct($studies, $sendTo) { $this->studies = $studies; $this->sendTo = $sendTo; + + Log::info('embargo_publish_trace', [ + 'stage' => 'study_publish_event_dispatched', + 'study_ids' => collect($studies)->pluck('id')->values()->all(), + 'send_to_count' => is_countable($sendTo) ? count($sendTo) : 0, + ]); } /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\Channel|array + * @return Channel|array */ public function broadcastOn(): array { diff --git a/app/Exceptions/EmbargoPublicationFailed.php b/app/Exceptions/EmbargoPublicationFailed.php new file mode 100644 index 000000000..cba539d53 --- /dev/null +++ b/app/Exceptions/EmbargoPublicationFailed.php @@ -0,0 +1,20 @@ +firstOrFail(); } elseif ($namespace == 'Dataset') { $model = Dataset::where([['identifier', $id]])->firstOrFail(); + } elseif ($namespace == 'Molecule') { + $model = Molecule::where([['identifier', $id]])->firstOrFail(); } return [ diff --git a/app/Http/Controllers/API/Auth/LoginController.php b/app/Http/Controllers/API/Auth/LoginController.php index 7964a0738..8ea21f9d7 100644 --- a/app/Http/Controllers/API/Auth/LoginController.php +++ b/app/Http/Controllers/API/Auth/LoginController.php @@ -88,21 +88,6 @@ class LoginController extends Controller * ), * * @OA\Response( - * response=403, - * description="Account not verified - Email verification required", - * - * @OA\JsonContent( - * - * @OA\Property( - * property="message", - * type="string", - * description="Error message indicating account verification status", - * example="Account is not yet verified. Please verify your email address by clicking on the link we just emailed to you." - * ) - * ) - * ), - * - * @OA\Response( * response=422, * description="Validation error - Invalid input data", * @@ -174,6 +159,10 @@ class LoginController extends Controller */ public function login(Request $request): JsonResponse { + $request->validate([ + 'email' => ['required', 'email'], + 'password' => ['required', 'string'], + ]); if (! Auth::attempt($request->only('email', 'password'))) { return response()->json([ diff --git a/app/Http/Controllers/API/Auth/RegisterController.php b/app/Http/Controllers/API/Auth/RegisterController.php index 12c6ffc44..17ae0b2c5 100644 --- a/app/Http/Controllers/API/Auth/RegisterController.php +++ b/app/Http/Controllers/API/Auth/RegisterController.php @@ -6,6 +6,7 @@ use App\Models\Team; use App\Models\User; use Illuminate\Auth\Events\Verified; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -258,7 +259,7 @@ class RegisterController extends Controller * * Register new user account * - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ public function register(Request $request) { @@ -276,7 +277,7 @@ public function register(Request $request) 'status' => false, 'message' => 'validation error', 'errors' => $validateUserDetails->errors(), - ], 401); + ], 422); } $authUser = auth('sanctum')->user(); diff --git a/app/Http/Controllers/API/Auth/UserController.php b/app/Http/Controllers/API/Auth/UserController.php index 16d1213e1..17830bf8d 100644 --- a/app/Http/Controllers/API/Auth/UserController.php +++ b/app/Http/Controllers/API/Auth/UserController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\API\Auth; use App\Http\Controllers\Controller; +use App\Models\User; use Illuminate\Http\Request; class UserController extends Controller @@ -214,7 +215,7 @@ class UserController extends Controller * * Get current authenticated user information * - * @return \App\Models\User + * @return User */ public function info(Request $request) { diff --git a/app/Http/Controllers/API/Auth/VerificationController.php b/app/Http/Controllers/API/Auth/VerificationController.php index 4c6e944ca..75fa26694 100644 --- a/app/Http/Controllers/API/Auth/VerificationController.php +++ b/app/Http/Controllers/API/Auth/VerificationController.php @@ -7,6 +7,8 @@ use Auth; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Events\Verified; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class VerificationController extends Controller @@ -129,7 +131,7 @@ class VerificationController extends Controller * Verify user email address using signed URL * * @param int $user_id - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse + * @return RedirectResponse|JsonResponse */ public function verify($user_id, Request $request) { @@ -144,7 +146,9 @@ public function verify($user_id, Request $request) } if ($request->user() && $request->user()->getKey() != $user_id) { - Auth::logout(); + if (method_exists(Auth::guard(), 'logout')) { + Auth::logout(); + } throw new AuthorizationException; } @@ -273,7 +277,7 @@ public function verify($user_id, Request $request) * * Resend email verification link * - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ public function resend() { diff --git a/app/Http/Controllers/API/DataController.php b/app/Http/Controllers/API/DataController.php index 15c0f13af..2a1aaccd5 100644 --- a/app/Http/Controllers/API/DataController.php +++ b/app/Http/Controllers/API/DataController.php @@ -9,7 +9,9 @@ use App\Models\Dataset; use App\Models\Project; use App\Models\Study; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Http\Resources\Json\AnonymousResourceCollection; use Spatie\QueryBuilder\QueryBuilder; class DataController extends Controller @@ -56,7 +58,7 @@ class DataController extends Controller * in="query", * description="Sort field with optional direction prefix (-created_at for descending)", * - * @OA\Schema(type="string", enum={"created_at", "-created_at", "identifier", "-identifier", "owner.email", "-owner.email"}, default="-created_at") + * @OA\Schema(type="string", enum={"created_at", "-created_at", "identifier", "-identifier"}, default="-created_at") * ), * * @OA\Parameter( @@ -76,14 +78,6 @@ class DataController extends Controller * ), * * @OA\Parameter( - * name="filter[owner.email]", - * in="query", - * description="Filter by data owner email", - * - * @OA\Schema(type="string", format="email", example="researcher@university.edu") - * ), - * - * @OA\Parameter( * name="filter[doi]", * in="query", * description="Filter by Digital Object Identifier", @@ -252,15 +246,15 @@ class DataController extends Controller * and includes Bioschemas.org metadata for enhanced discoverability. * * @param string $model The data model type (projects|samples|datasets) - * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection + * @return AnonymousResourceCollection */ public function all(Request $request, $model) { $per_page = \Request::get('per_page') ?: 100; $defaultSort = '-created_at'; - $allowedSorts = ['created_at', 'identifier', 'owner.email']; - $allowedFilters = ['name', 'created_at', 'identifier', 'owner.email', 'doi']; + $allowedSorts = ['created_at', 'identifier']; + $allowedFilters = ['name', 'created_at', 'identifier', 'doi']; if ($model === 'projects') { return ProjectResource::collection( QueryBuilder::for(Project::class) @@ -482,7 +476,7 @@ public function all(Request $request, $model) * - Usage statistics and engagement metrics * * @param string $id NMRXIV identifier (P123, S456, D789) - * @return \App\Http\Resources\ProjectResource|\App\Http\Resources\StudyResource|\App\Http\Resources\DatasetResource|\Illuminate\Http\JsonResponse + * @return ProjectResource|StudyResource|DatasetResource|JsonResponse */ public function id(Request $request, $id) { diff --git a/app/Http/Controllers/API/ELNController.php b/app/Http/Controllers/API/ELNController.php index 6af9947da..bb7cd1ca3 100644 --- a/app/Http/Controllers/API/ELNController.php +++ b/app/Http/Controllers/API/ELNController.php @@ -11,6 +11,7 @@ use App\Services\ChemotionRepositoryTrackerService; use Carbon\Carbon; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; @@ -21,6 +22,7 @@ class ELNController extends Controller */ const SUPPORTED_ELNS = [ 'chemotion', + 'nobs', ]; /** @@ -29,7 +31,7 @@ class ELNController extends Controller * operationId="uploadELNData", * tags={"ELN Submission"}, * summary="Upload and process data from Electronic Lab Notebook (ELN) systems", - * description="Creates or updates a draft with data from external ELN systems. Currently supports Chemotion. Processes ZIP files containing experimental data and extracts them to organized folder structure.", + * description="Creates or updates a draft with data from external ELN systems. Currently supports Chemotion and NoBs. Processes ZIP files containing experimental data and extracts them to organized folder structure.", * security={{"sanctum": {}}}, * * @OA\Parameter( @@ -40,7 +42,7 @@ class ELNController extends Controller * * @OA\Schema( * type="string", - * enum={"chemotion"}, + * enum={"chemotion", "nobs"}, * example="chemotion" * ) * ), @@ -170,7 +172,7 @@ class ELNController extends Controller * type="array", * * @OA\Items(type="string"), - * example={"chemotion"} + * example={"chemotion", "nobs"} * ) * ) * ), @@ -207,7 +209,7 @@ class ELNController extends Controller * Handle file upload for a specific ELN entry * * @param string $eln - * @return \Illuminate\Http\Response + * @return Response */ public function upload($eln, Request $request) { @@ -355,7 +357,7 @@ public function upload($eln, Request $request) * * @OA\Schema( * type="string", - * enum={"chemotion"}, + * enum={"chemotion", "nobs"}, * example="chemotion" * ) * ), @@ -497,7 +499,7 @@ public function upload($eln, Request $request) * type="array", * * @OA\Items(type="string"), - * example={"chemotion"} + * example={"chemotion", "nobs"} * ) * ) * ), @@ -540,7 +542,7 @@ public function upload($eln, Request $request) * * @param string $eln * @param string $external_id - * @return \Illuminate\Http\Response + * @return Response */ public function status($eln, $external_id) { diff --git a/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasController.php b/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasController.php index c941d3788..d5fdf189b 100644 --- a/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasController.php +++ b/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasController.php @@ -12,6 +12,7 @@ use App\Models\User; use Carbon\Carbon; use Illuminate\Auth\Access\AuthorizationException; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; use Spatie\SchemaOrg\Schema; @@ -240,7 +241,7 @@ class BioschemasController extends Controller * * @param string $username NMRXIV username * @param string $projectName Project slug identifier - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ // public function modelSchemaByName(Request $request, $username, $projectName, $studyName = null, $datasetName = null) @@ -398,7 +399,7 @@ class BioschemasController extends Controller * @OA\Property(property="name", type="string", example="magnetic field strength"), * @OA\Property(property="value", type="number", example=14.1), * @OA\Property(property="unitText", type="string", example="Tesla"), - * @OA\Property(property="propertyID", type="string", example="MR:1400253") + * @OA\Property(property="propertyID", type="string", example="NMR:1400253") * ) * ), * @OA\Property( @@ -487,7 +488,7 @@ class BioschemasController extends Controller * - Scientific workflow interoperability * * @param string $identifier NMRXIV public identifier (P123, S456, D789) - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ public function modelSchemaByID(Request $request, $identifier) { @@ -577,7 +578,7 @@ public function getSample($study) $sampleSchema['dct:conformsTo'] = BioschemasHelper::conformsTo(['https://bioschemas.org/types/ChemicalSubstance/0.3-RELEASE-2019_09_02']); $sampleSchema->name($sample->name); $sampleSchema->description($sample->description); - $sampleSchema->url(env('APP_URL').'/'.explode(':', $study->identifier ? $study->identifier : ':')[1]); + $sampleSchema->url(config('app.url').'/'.explode(':', $study->identifier ? $study->identifier : ':')[1]); $sampleSchema->hasBioChemEntityPart($this->prepareMoleculesSchemas($sample)); return $sampleSchema; @@ -707,7 +708,7 @@ public function prepareNMRiumInfo($dataset) // $experimentProperty = BioschemasHelper::preparePropertyValue('pulsed nuclear magnetic resonance spectroscopy', 'CHMO:0000613', $experiment, null); $temperatureProperty = BioschemasHelper::preparePropertyValue('Temperature', 'NCIT:C25206', $temperature, 'http://purl.obolibrary.org/obo/UO_0000012'); $baseFrequencyProperty = BioschemasHelper::preparePropertyValue('irradiation frequency', 'NMR:1400026', $baseFrequency, 'http://purl.obolibrary.org/obo/UO_0000325'); - $fieldStrengthProperty = BioschemasHelper::preparePropertyValue('magnetic field strength', 'MR:1400253', $fieldStrength, 'http://purl.obolibrary.org/obo/UO_0000228'); + $fieldStrengthProperty = BioschemasHelper::preparePropertyValue('magnetic field strength', 'NMR:1400253', $fieldStrength, 'http://purl.obolibrary.org/obo/UO_0000228'); $numberOfScansProperty = BioschemasHelper::preparePropertyValue('number of scans', 'NMR:1400087', $numberOfScans, 'scans'); $pulseSequenceProperty = BioschemasHelper::preparePropertyValue('nuclear magnetic resonance pulse sequence', 'CHMO:0001841', $pulseSequence, null); $spectralWidthProperty = BioschemasHelper::preparePropertyValue('Spectral Width', 'NCIT:C156496', $spectralWidth, 'http://purl.obolibrary.org/obo/UO_0000169'); @@ -791,7 +792,7 @@ public function datasetLite($dataset) $datasetSchema->description($dataset->description); $datasetSchema->keywords($nmriumInfo[0]); $datasetSchema->license($dataset->study->license->url); - $datasetSchema->url(env('APP_URL').'/'.explode(':', $dataset->identifier ? $dataset->identifier : ':')[1]); + $datasetSchema->url(config('app.url').'/'.explode(':', $dataset->identifier ? $dataset->identifier : ':')[1]); $datasetSchema->dateCreated($dataset->created_at ? $dataset->created_at->toISOString() : null); $datasetSchema->dateModified($dataset->updated_at ? $dataset->updated_at->toISOString() : null); $datasetSchema->datePublished($dataset->release_date ? Carbon::parse($dataset->release_date)->toISOString() : null); @@ -846,7 +847,7 @@ public function studyLite($study) $studySchema->description($study->description); $studySchema->keywords(BioschemasHelper::getTags($study)); $studySchema->license($study->license->url); - $studySchema->url(env('APP_URL').'/'.explode(':', $study->identifier ? $study->identifier : ':')[1]); + $studySchema->url(config('app.url').'/'.explode(':', $study->identifier ? $study->identifier : ':')[1]); $studySchema->dateCreated($study->created_at ? $study->created_at->toISOString() : null); $studySchema->dateModified($study->updated_at ? $study->updated_at->toISOString() : null); $studySchema->datePublished($study->release_date ? Carbon::parse($study->release_date)->toISOString() : null); @@ -894,7 +895,7 @@ public function projectLite($project) $projectSchema->keywords(BioschemasHelper::getTags($project)); $projectSchema->license($project->license->url); $projectSchema->publisher(BioschemasHelper::preparePublisher()); - $projectSchema->url(env('APP_URL').'/'.explode(':', $project->identifier ? $project->identifier : ':')[1]); + $projectSchema->url(config('app.url').'/'.explode(':', $project->identifier ? $project->identifier : ':')[1]); $projectSchema->dateCreated($project->created_at ? $project->created_at->toISOString() : null); $projectSchema->dateModified($project->updated_at ? $project->updated_at->toISOString() : null); $projectSchema->datePublished($project->release_date ? Carbon::parse($project->release_date)->toISOString() : null); diff --git a/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasHelper.php b/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasHelper.php index bdb4710c2..a61242a19 100644 --- a/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasHelper.php +++ b/app/Http/Controllers/API/Schemas/Bioschemas/BioschemasHelper.php @@ -2,6 +2,9 @@ namespace App\Http\Controllers\API\Schemas\Bioschemas; +use App\Models\Dataset; +use App\Models\NMRium; +use Illuminate\Support\Facades\DB; use Spatie\SchemaOrg\Schema; class BioschemasHelper @@ -146,7 +149,7 @@ public static function prepareCitations($model) */ public static function prepareDataDownload($dataset) { - $url = env('APP_URL'); + $url = config('app.url'); $user = $dataset->owner->username; if (property_exists($dataset, 'project')) { $slug = $dataset->project->slug; @@ -175,8 +178,8 @@ public static function prepareDataDownload($dataset) public static function preparePublisher() { $publisherSchema = Schema::Organization(); - $publisherSchema->name(env('APP_NAME')); - $publisherSchema->url(env('APP_URL')); + $publisherSchema->name(config('app.name')); + $publisherSchema->url(config('app.url')); return $publisherSchema; } @@ -192,50 +195,228 @@ public static function preparePublisher() public static function prepareDataCatalogLite() { $dataCatalogSchema = Schema::DataCatalog(); - $dataCatalogSchema->name(env('APP_NAME')); - $dataCatalogSchema->url(env('APP_URL')); + $dataCatalogSchema->name(config('app.name')); + $dataCatalogSchema->url(config('app.url')); return $dataCatalogSchema; } /** - * Get NMRium info from a dataset. + * Get NMRium spectrum `info` for a dataset (read-only). * - * @param App\Models\Dataset $dataset - * @return object $info + * Uses the dataset's own `nmrium` row when present. Otherwise returns the + * first matching spectrum's `info` from the parent study's stored NMRium + * JSON without persisting — persistence happens only when the study + * payload is saved via {@see self::syncDatasetNmriumFromStudyPayload()}. */ - public static function getNMRiumInfo($dataset) + public static function getNMRiumInfo(Dataset $dataset): ?object { - $info = null; - $nmrium = $dataset->nmrium; - if (! $nmrium) { - $study = $dataset->study; - if ($study->nmrium) { - $NMRiumInfo = (object) json_decode(json_encode($study->nmrium->nmrium_info)); - foreach ($NMRiumInfo->data->spectra as $spectra) { - $fileSource = $spectra->sourceSelector->files[0]; - $fileName = pathinfo($fileSource); - if ($fileName['basename'] == $dataset->fsObject->name) { - $info = $spectra->info; - } - } + $dataset->loadMissing([ + 'nmrium', + 'study.nmrium', + 'study.sample', + 'study.draft', + 'fsObject', + 'study.fsObject', + ]); + + if ($dataset->nmrium) { + $rawInfo = self::extractPrimaryInfoFromNmriumInfo($dataset->nmrium->nmrium_info); + $normalized = self::normalizeSpectrumInfo($rawInfo); + if ($normalized !== null) { + return $normalized; } - } else { - $NMRiumInfo = (object) json_decode(json_encode($nmrium->nmrium_info)); - // check if data key exists - if (isset($NMRiumInfo->data)) { - $nmriumData = $NMRiumInfo->data; - if ($nmriumData) { - $spectraData = $nmriumData->spectra[0]; - $info = $spectraData->info; - } else { - $spectraData = json_decode($NMRiumInfo->scalar)->data->spectra[0]; - $info = $NMRiumInfo->data; + } + + $matched = self::collectStudySpectraMatchingDataset($dataset); + if ($matched === []) { + return null; + } + + $first = $matched[0]; + $info = is_array($first) ? ($first['info'] ?? null) : null; + + return self::normalizeSpectrumInfo($info); + } + + /** + * Collect spectra from a study-level NMRium JSON payload that belong to + * this dataset (robust path match on `FileSystemObject::relative_url`). + * + * @param array $nmriumInfo + * @return list> + */ + public static function collectStudySpectraMatchingDatasetFromPayload(Dataset $dataset, array $nmriumInfo): array + { + $study = $dataset->study; + if (! $study) { + return []; + } + + if (! isset($nmriumInfo['data']['spectra']) || ! is_array($nmriumInfo['data']['spectra'])) { + return []; + } + + $studyFSObject = $study->fsObject; + $datasetFSObject = $dataset->fsObject; + if (! $studyFSObject || ! $datasetFSObject) { + return []; + } + + $draft = $study->relationLoaded('draft') ? $study->draft : $study->draft()->first(); + $isChemotion = $draft && ($draft->eln === 'chemotion'); + $parentName = $isChemotion ? optional($datasetFSObject->parent)->name : null; + if ($isChemotion && $parentName === null) { + return []; + } + + $datasetRelativeUrl = $datasetFSObject->relative_url; + if (! is_string($datasetRelativeUrl) || $datasetRelativeUrl === '') { + $datasetRelativeUrl = $isChemotion + ? '/'.$studyFSObject->name.'/'.$parentName.'/'.$datasetFSObject->name + : '/'.$studyFSObject->name.'/'.$datasetFSObject->name; + } + $path = rtrim($datasetRelativeUrl, '/'); + $isDatasetFile = $datasetFSObject->type === 'file'; + $needle = $isDatasetFile ? $path : $path.'/'; + + $matchedSpectra = []; + foreach ($nmriumInfo['data']['spectra'] as $spectra) { + if (! is_array($spectra)) { + continue; + } + $selector = $spectra['sourceSelector'] ?? $spectra['selector'] ?? []; + $files = $selector['files'] ?? []; + if (! is_array($files)) { + continue; + } + $hit = false; + foreach ($files as $file) { + if (! is_string($file)) { + continue; } + $pathsMatch = $isDatasetFile + ? str_ends_with($file, $needle) + : str_contains($file, $needle); + if ($pathsMatch) { + $hit = true; + break; + } + } + if ($hit) { + $matchedSpectra[] = $spectra; + } + } + + return $matchedSpectra; + } + + /** + * Collect spectra entries from the parent study's stored NMRium JSON. + * + * @return list> + */ + public static function collectStudySpectraMatchingDataset(Dataset $dataset): array + { + $study = $dataset->study; + if (! $study || ! $study->nmrium) { + return []; + } + + $nmriumInfo = $study->nmrium->nmrium_info; + if (! is_array($nmriumInfo)) { + return []; + } + + return self::collectStudySpectraMatchingDatasetFromPayload($dataset, $nmriumInfo); + } + + /** + * Persist matched study spectra onto the dataset. Intended for the study + * NMRium save path only — pass the merged study JSON (e.g. request payload + * after sample/molecule merge) so matching uses the same data being stored. + * + * @param array $mergedStudyNmriumInfo + * @return list> Matched spectrum entries (empty if none) + */ + public static function syncDatasetNmriumFromStudyPayload(Dataset $dataset, array $mergedStudyNmriumInfo): array + { + $dataset->loadMissing([ + 'nmrium', + 'study', + 'fsObject', + 'study.fsObject', + 'study.draft', + ]); + + $matched = self::collectStudySpectraMatchingDatasetFromPayload($dataset, $mergedStudyNmriumInfo); + if ($matched === []) { + return []; + } + + $base = json_decode(json_encode($mergedStudyNmriumInfo), true); + if (! is_array($base)) { + return []; + } + + $base['data']['spectra'] = $matched; + + DB::transaction(function () use ($dataset, $base) { + $dataset->refresh(); + $dataset->load('nmrium'); + + if ($dataset->nmrium) { + $dataset->nmrium->nmrium_info = $base; + $dataset->nmrium->save(); + } else { + $nmrium = NMRium::create([ + 'nmrium_info' => $base, + ]); + $dataset->nmrium()->save($nmrium); } + + $dataset->forceFill(['has_nmrium' => true])->save(); + }); + + return $matched; + } + + /** + * @param mixed $nmriumInfo + */ + private static function extractPrimaryInfoFromNmriumInfo($nmriumInfo): mixed + { + if ($nmriumInfo === null) { + return null; + } + $decoded = is_array($nmriumInfo) + ? $nmriumInfo + : json_decode(json_encode($nmriumInfo), true); + if (! is_array($decoded)) { + return null; + } + if (isset($decoded['data']['spectra'][0]['info'])) { + return $decoded['data']['spectra'][0]['info']; + } + + return null; + } + + private static function normalizeSpectrumInfo(mixed $info): ?object + { + if ($info === null) { + return null; + } + if (is_object($info)) { + return $info; + } + if (is_array($info)) { + $obj = json_decode(json_encode($info), false); + + return is_object($obj) ? $obj : null; } - return $info; + return null; } /** diff --git a/app/Http/Controllers/API/Schemas/Bioschemas/DataCatalogController.php b/app/Http/Controllers/API/Schemas/Bioschemas/DataCatalogController.php index cda487f81..29d8688a8 100644 --- a/app/Http/Controllers/API/Schemas/Bioschemas/DataCatalogController.php +++ b/app/Http/Controllers/API/Schemas/Bioschemas/DataCatalogController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\API\Schemas\Bioschemas; use App\Http\Controllers\Controller; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Config; use Spatie\SchemaOrg\Schema; @@ -246,7 +247,7 @@ class DataCatalogController extends Controller * - Compliance with funding agency data sharing requirements * - Support for systematic reviews and meta-analyses in chemistry * - * @return \Illuminate\Http\JsonResponse DataCatalog schema representing NMRXIV repository + * @return JsonResponse DataCatalog schema representing NMRXIV repository */ public function dataCatalogSchema(Request $request) { @@ -254,13 +255,13 @@ public function dataCatalogSchema(Request $request) $contributors = $this->prepareContributors(); $nmrXivProvider = Schema::Organization(); - $nmrXivProvider->name(Config::get('schemas.bioschema.provider')); - $nmrXivProvider->url(Config::get('schemas.bioschema.provider_url')); + $nmrXivProvider->name(Config::get('schemas.bioschemas.provider')); + $nmrXivProvider->url(Config::get('schemas.bioschemas.provider_url')); $dataCatalogSchema = Schema::DataCatalog(); - $dataCatalogSchema['@id'] = url(Config::get('app.url')); + $dataCatalogSchema['@id'] = Config::get('app.url'); $dataCatalogSchema['dct:conformsTo'] = BioschemasHelper::conformsTo(['https://schema.org/DataCatalog']); - $dataCatalogSchema->description(env('APP_DESCRIPTION')); + $dataCatalogSchema->description(Config::get('app.description', 'NMRXIV is an open-access preprint repository for sharing and discovering nuclear magnetic resonance (NMR) spectroscopy data.')); $dataCatalogSchema->keywords($keywords); $dataCatalogSchema->name(Config::get('app.name')); $dataCatalogSchema->provider($nmrXivProvider); diff --git a/app/Http/Controllers/API/Schemas/DataCite/DataCiteController.php b/app/Http/Controllers/API/Schemas/DataCite/DataCiteController.php index 0c0f3dfcc..3a515f089 100644 --- a/app/Http/Controllers/API/Schemas/DataCite/DataCiteController.php +++ b/app/Http/Controllers/API/Schemas/DataCite/DataCiteController.php @@ -7,6 +7,7 @@ use App\Models\Project; use App\Models\Study; use App\Models\User; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; class DataCiteController extends Controller @@ -363,7 +364,7 @@ class DataCiteController extends Controller * * @param string $username NMRXIV username * @param string $projectName Project slug identifier - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ // public function modelSchemaByName(Request $request, $username, $projectName, $studyName = null, $datasetName = null) @@ -620,7 +621,7 @@ class DataCiteController extends Controller * - **Datasets**: Specific NMR experiments and spectroscopic data * * @param string $identifier NMRXIV public identifier (P123, S456, D789) - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ public function modelSchemaByID(Request $request, $identifier) { diff --git a/app/Http/Controllers/API/SearchController.php b/app/Http/Controllers/API/SearchController.php index c164e7363..7f3306b07 100644 --- a/app/Http/Controllers/API/SearchController.php +++ b/app/Http/Controllers/API/SearchController.php @@ -4,7 +4,9 @@ use App\Http\Controllers\Controller; use App\Models\Molecule; +use App\Support\Public\PublicMoleculeAggregates; use Illuminate\Database\QueryException; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\DB; @@ -225,7 +227,7 @@ class SearchController extends Controller * - **Tags**: Classification-based search * - **Filters**: Property-based filtering * - * @return \Illuminate\Pagination\LengthAwarePaginator|\Illuminate\Http\JsonResponse + * @return LengthAwarePaginator|JsonResponse */ public function search(Request $request) { @@ -261,6 +263,10 @@ public function search(Request $request) $query = $this->sanitizeQuery($request->get('query')); + if (($query === null || $query === '') && $sort === null) { + $sort = 'recent'; + } + $type = $request->query('type') ? $request->query('type') : $request->get('type'); @@ -333,12 +339,17 @@ public function search(Request $request) $queryType = strtolower($queryType); + $publicSpectraFilter = ' AND '.PublicMoleculeAggregates::hasPublicSpectraExistsSql('molecules.id'); + $statement = null; if ($queryType == 'smiles' || $queryType == 'substructure') { try { $hits = DB::select( - 'SELECT id, COUNT(*) OVER () as count FROM mols WHERE m@>? LIMIT ? OFFSET ?', + "SELECT mols.id, COUNT(*) OVER () as count FROM mols + INNER JOIN molecules ON molecules.id = mols.id + WHERE m@>? AND molecules.identifier IS NOT NULL{$publicSpectraFilter} + LIMIT ? OFFSET ?", [$query, $limit, $offset] ); } catch (\Exception $e) { @@ -348,18 +359,21 @@ public function search(Request $request) } } elseif ($queryType == 'inchi') { $hits = DB::select( - 'SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL AND standard_inchi LIKE ? LIMIT ? OFFSET ?', + "SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL AND standard_inchi LIKE ?{$publicSpectraFilter} LIMIT ? OFFSET ?", ['%'.$query.'%', $limit, $offset] ); } elseif ($queryType == 'inchikey') { $hits = DB::select( - 'SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL AND standard_inchi_key LIKE ? LIMIT ? OFFSET ?', + "SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL AND standard_inchi_key LIKE ?{$publicSpectraFilter} LIMIT ? OFFSET ?", ['%'.$query.'%', $limit, $offset] ); } elseif ($queryType == 'exact') { try { $hits = DB::select( - 'SELECT id, COUNT(*) OVER () as count FROM mols WHERE m@=? LIMIT ? OFFSET ?', + "SELECT mols.id, COUNT(*) OVER () as count FROM mols + INNER JOIN molecules ON molecules.id = mols.id + WHERE m@=? AND molecules.identifier IS NOT NULL{$publicSpectraFilter} + LIMIT ? OFFSET ?", [$query, $limit, $offset] ); } catch (\Exception $e) { @@ -369,7 +383,10 @@ public function search(Request $request) } elseif ($queryType == 'similarity') { try { $hits = DB::select( - 'SELECT id, COUNT(*) OVER () as count FROM fps WHERE mfp2%morganbv_fp(?) LIMIT ? OFFSET ?', + "SELECT fps.id, COUNT(*) OVER () as count FROM fps + INNER JOIN molecules ON molecules.id = fps.id + WHERE mfp2%morganbv_fp(?) AND molecules.identifier IS NOT NULL{$publicSpectraFilter} + LIMIT ? OFFSET ?", [$query, $limit, $offset] ); } catch (\Exception $e) { @@ -377,8 +394,18 @@ public function search(Request $request) $hits = []; } } elseif ($queryType == 'tags') { - $results = Molecule::withAnyTags([$query], $tagType)->paginate($limit)->items(); - $count = Molecule::withAnyTags([$query], $tagType)->count(); + $tagQuery = PublicMoleculeAggregates::scopePublicCatalog( + Molecule::withAnyTags([$query], $tagType) + ); + if ($sort === 'recent') { + $tagQuery->orderByDesc('created_at'); + } + $results = PublicMoleculeAggregates::enrich( + $tagQuery->paginate($limit)->items() + ); + $count = PublicMoleculeAggregates::scopePublicCatalog( + Molecule::withAnyTags([$query], $tagType) + )->count(); } elseif ($queryType == 'filters') { $result = $this->buildSecureFilterQuery($query, $filterMap, $limit, $offset); $hits = $result['hits']; @@ -386,12 +413,13 @@ public function search(Request $request) } else { if ($query) { $hits = DB::select( - 'SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL AND (name::TEXT ILIKE ? OR synonyms::TEXT ILIKE ? OR identifier::TEXT ILIKE ?) LIMIT ? OFFSET ?', - ['%'.$query.'%', '%'.$query.'%', '%'.$query.'%', $limit, $offset] + "SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL AND (name::TEXT ILIKE ? OR iupac_name ILIKE ? OR synonyms::TEXT ILIKE ? OR identifier::TEXT ILIKE ?){$publicSpectraFilter} LIMIT ? OFFSET ?", + ['%'.$query.'%', '%'.$query.'%', '%'.$query.'%', '%'.$query.'%', $limit, $offset] ); } else { + $orderBy = $sort === 'recent' ? 'ORDER BY created_at DESC' : ''; $hits = DB::select( - 'SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL LIMIT ? OFFSET ?', + "SELECT id, COUNT(*) OVER () as count FROM molecules WHERE identifier IS NOT NULL{$publicSpectraFilter} {$orderBy} LIMIT ? OFFSET ?", [$limit, $offset] ); } @@ -431,14 +459,27 @@ public function search(Request $request) $results = []; } } + + if ($results !== []) { + $results = PublicMoleculeAggregates::enrich($results); + } + $pagination = new LengthAwarePaginator( $results, $count, $limit, - $page + $page, + ['path' => url('/compounds')] ); - // dd($pagination); + $pagination->appends(array_filter([ + 'query' => $query !== null && $query !== '' ? $query : null, + 'sort' => $sort, + 'limit' => $limit !== 24 ? $limit : null, + 'tagType' => $tagType, + 'type' => $queryType !== 'text' ? $queryType : null, + ], fn ($value) => $value !== null && $value !== '')); + return $pagination; } catch (QueryException $exception) { return response()->json( @@ -571,7 +612,14 @@ private function buildSecureFilterQuery(string $query, array $filterMap, int $li } $whereClause = implode(' OR ', $whereConditions); - $sql = "SELECT molecule_id as id, COUNT(*) OVER () as count FROM properties WHERE {$whereClause} LIMIT ? OFFSET ?"; + $publicSpectraFilter = PublicMoleculeAggregates::hasPublicSpectraExistsSql('molecules.id'); + $sql = "SELECT properties.molecule_id as id, COUNT(*) OVER () as count + FROM properties + INNER JOIN molecules ON molecules.id = properties.molecule_id + WHERE ({$whereClause}) + AND molecules.identifier IS NOT NULL + AND {$publicSpectraFilter} + LIMIT ? OFFSET ?"; $parameters[] = $limit; $parameters[] = $offset; diff --git a/app/Http/Controllers/Admin/AnnouncementController.php b/app/Http/Controllers/Admin/AnnouncementController.php index e3d13cea1..25d6d2d1d 100644 --- a/app/Http/Controllers/Admin/AnnouncementController.php +++ b/app/Http/Controllers/Admin/AnnouncementController.php @@ -6,16 +6,18 @@ use App\Models\Announcement; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; use Inertia\Inertia; +use Pages\Announcement\Index; class AnnouncementController extends Controller { /** * Show all the list of Announcements created. * - * @return \Pages\Announcement\Index + * @return Index */ public function index(Request $request) { @@ -44,7 +46,7 @@ public function index(Request $request) /** * Create the new entry for the announcement. * - * @return \Illuminate\Http\Response + * @return Response */ public function create(Request $request) { @@ -84,7 +86,7 @@ public function create(Request $request) /** * Update the specified announcement in the storage. * - * @return \Illuminate\Http\Response + * @return Response */ public function update(Request $request, Announcement $announcement) { @@ -117,7 +119,7 @@ public function update(Request $request, Announcement $announcement) /** * Remove the specified announcement from the storage. * - * @return \Illuminate\Http\Response + * @return Response */ public function destroy(Request $request, Announcement $announcement) { diff --git a/app/Http/Controllers/Admin/UsersController.php b/app/Http/Controllers/Admin/UsersController.php index 131469a06..d69fa5c36 100644 --- a/app/Http/Controllers/Admin/UsersController.php +++ b/app/Http/Controllers/Admin/UsersController.php @@ -4,15 +4,21 @@ use App\Actions\Fortify\CreateNewUser; use App\Actions\Fortify\PasswordValidationRules; +use App\Actions\Fortify\UpdateUserProfileInformation; use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Auth\Events\Registered; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Inertia\Inertia; use Laravel\Fortify\Contracts\UpdatesUserProfileInformation; +use Pages\Console\Users\Create; +use Pages\Console\Users\Edit; +use Pages\Console\Users\Index; use Spatie\Permission\Models\Role; class UsersController extends Controller @@ -22,7 +28,7 @@ class UsersController extends Controller /** * Render the index page with list of users. * - * @return \Pages\Console\Users\Index + * @return Index */ public function index(Request $request) { @@ -42,6 +48,7 @@ public function index(Request $request) 'role' => $user->getRoleNames(), 'orcid_id' => $user->orcid_id, 'affiliation' => $user->affiliation, + 'ror_id' => $user->ror_id, ]; }), 'roles' => Role::orderBy('name') @@ -54,7 +61,7 @@ public function index(Request $request) /** * Render the create user page. * - * @return \Pages\Console\Users\Create + * @return Create */ public function create() { @@ -70,7 +77,7 @@ public function create() /** * Store the newly created user. * - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function store(Request $request, CreateNewUser $creator) { @@ -83,7 +90,7 @@ public function store(Request $request, CreateNewUser $creator) /** * Render the edit user page. * - * @return \Pages\Console\Users\Edit + * @return Edit */ public function edit(User $user) { @@ -97,6 +104,7 @@ public function edit(User $user) 'profile_photo_url' => $user->profile_photo_url, 'orcid_id' => $user->orcid_id, 'affiliation' => $user->affiliation, + 'ror_id' => $user->ror_id, ], ]); @@ -105,8 +113,8 @@ public function edit(User $user) /** * Save the updated user info. * - * @param \App\Actions\Fortify\UpdateUserProfileInformation $updater - * @return \Illuminate\Http\RedirectResponse + * @param UpdateUserProfileInformation $updater + * @return RedirectResponse */ public function update(User $user, Request $request, UpdatesUserProfileInformation $updater) { @@ -118,7 +126,7 @@ public function update(User $user, Request $request, UpdatesUserProfileInformati /** * Save the updated password for the user. * - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function updatePassword(User $user, Request $request) { @@ -136,7 +144,7 @@ public function updatePassword(User $user, Request $request) /** * Save the updated role for the user. * - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function updateRole(User $user, Request $request) { @@ -162,7 +170,7 @@ public function updateRole(User $user, Request $request) /** * Check if user has password. * - * @return \Illuminate\Http\Response + * @return Response */ public function checkPassword(Request $request) { @@ -180,7 +188,7 @@ public function checkPassword(Request $request) /** * Delete the profile photo. * - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function destroyPhoto(User $user, Request $request) { @@ -193,7 +201,7 @@ public function destroyPhoto(User $user, Request $request) * Mark notification as read. * * @param int $id - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function markNotificationAsRead(User $user, Request $request) { @@ -210,7 +218,7 @@ public function markNotificationAsRead(User $user, Request $request) * Mark all notification as read. * * @param User $user - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function markAllNotificationAsRead(Request $request) { diff --git a/app/Http/Controllers/ApplicationController.php b/app/Http/Controllers/ApplicationController.php index 022a698db..f5babaf0a 100644 --- a/app/Http/Controllers/ApplicationController.php +++ b/app/Http/Controllers/ApplicationController.php @@ -2,13 +2,17 @@ namespace App\Http\Controllers; +use App\Actions\License\GetLicense; use App\Http\Resources\DatasetResource; use App\Http\Resources\ProjectResource; use App\Http\Resources\StudyResource; +use App\Models\Project; +use App\Support\ProjectWorkspace; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\Request; use Illuminate\Support\Facades\Gate; use Inertia\Inertia; +use Inertia\Response as InertiaResponse; class ApplicationController extends Controller { @@ -22,7 +26,7 @@ public function compounds(Request $request) { $query = $request->get('query'); $limit = $request->get('limit') ? $request->get('limit') : 24; - $page = $request->query('page'); + $page = max(1, (int) $request->query('page', 1)); $tagType = $request->query('tagType') ? $request->query('tagType') : null; return Inertia::render('Public/Compounds', compact(['query', 'limit', 'page', 'tagType'])); @@ -40,9 +44,11 @@ public function resolveCompound(Request $request, $identifier) $model = $resolvedModel['model']; if ($model && $namespace === 'Molecule') { - // Redirect to spectra page with compound parameter for now - // This maintains the current compound viewing functionality - return redirect('/spectra?compound='.substr($identifier, 1)); + $compoundId = $model->getRawOriginal('identifier'); + + return app(StudyController::class)->publicStudiesView( + $request->merge(['compound' => $compoundId]) + ); } else { abort(404, 'Compound not found'); } @@ -53,9 +59,9 @@ public function resolveCompound(Request $request, $identifier) * * @return Inertia\Inertia */ - public function resolveSample(Request $request, $identifier) + public function resolveSample(Request $request, $identifier, GetLicense $getLicense) { - return $this->resolve($request, $identifier); + return $this->resolve($request, $identifier, $getLicense); } /** @@ -63,9 +69,9 @@ public function resolveSample(Request $request, $identifier) * * @return Inertia\Inertia */ - public function resolveProject(Request $request, $identifier) + public function resolveProject(Request $request, $identifier, GetLicense $getLicense) { - return $this->resolve($request, $identifier); + return $this->resolve($request, $identifier, $getLicense); } /** @@ -73,9 +79,9 @@ public function resolveProject(Request $request, $identifier) * * @return Inertia\Inertia */ - public function resolveDataset(Request $request, $identifier) + public function resolveDataset(Request $request, $identifier, GetLicense $getLicense) { - return $this->resolve($request, $identifier); + return $this->resolve($request, $identifier, $getLicense); } /** @@ -84,13 +90,12 @@ public function resolveDataset(Request $request, $identifier) * * @return Inertia\Inertia */ - public function resolve(Request $request, $identifier) + public function resolve(Request $request, $identifier, GetLicense $getLicense) { $resolvedModel = resolveIdentifier($identifier); $namespace = $resolvedModel['namespace']; $model = $resolvedModel['model']; if ($model) { - $studyId = $request->get('id'); $tab = $request->get('tab'); if ($namespace == 'Project') { @@ -102,74 +107,130 @@ public function resolve(Request $request, $identifier) } } elseif ($namespace == 'Study') { $study = $model; + $study->load(['studyAuthors', 'linkedCitations']); $project = $study->project; $tab = 'study'; } elseif ($namespace == 'Dataset') { $dataset = $model; + $dataset->loadMissing(['nmrium', 'study.sample']); $study = $dataset->study; - $project = $dataset->project; + if (! $study) { + abort(404, 'Page not found'); + } + $project = $dataset->project ?? $study->project; $tab = 'dataset'; } switch ($tab) { case 'info': - return Inertia::render('Public/Project/Show', [ - 'project' => (new ProjectResource($project))->lite(false, ['users', 'authors', 'citations']), - 'tab' => $tab, - ]); - break; + return $this->renderPublicProject( + 'Public/Project/Show', + [ + 'project' => (new ProjectResource($project))->lite(false, ['users', 'authors', 'citations']), + 'tab' => $tab, + ], + $request, + $project, + $getLicense + ); case 'samples': - return Inertia::render('Public/Project/Samples', [ - 'project' => (new ProjectResource($project))->lite(false, []), - 'tab' => $tab, - ]); - break; - case 'files': - return Inertia::render('Public/Project/Files', [ - 'project' => (new ProjectResource($project))->lite(false, ['files']), - 'tab' => $tab, - ]); - break; - case 'license': - return Inertia::render('Public/Project/License', [ - 'project' => (new ProjectResource($project))->lite(false, ['license']), - 'tab' => $tab, - ]); - break; - case 'study': - if ($project) { - return Inertia::render('Public/Project/Study', [ + return $this->renderPublicProject( + 'Public/Project/Samples', + [ 'project' => (new ProjectResource($project))->lite(false, []), 'tab' => $tab, - 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'datasets', 'molecules']), - ]); - break; - } else { - return Inertia::render('Public/Sample/Show', [ + ], + $request, + $project, + $getLicense + ); + case 'files': + return $this->renderPublicProject( + 'Public/Project/Files', + [ + 'project' => (new ProjectResource($project))->lite(false, ['files']), 'tab' => $tab, - 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'datasets', 'molecules', 'owner', 'license']), - ]); - break; + ], + $request, + $project, + $getLicense + ); + case 'study': + if ($project) { + return $this->renderPublicProject( + 'Public/Project/Study', + [ + 'project' => (new ProjectResource($project))->lite(false, []), + 'tab' => $tab, + 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'datasets', 'molecules', 'citations']), + ], + $request, + $project, + $getLicense + ); } + + return Inertia::render('Public/Sample/Show', [ + 'tab' => $tab, + 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'datasets', 'molecules', 'owner', 'license', 'authors', 'citations']), + ]); case 'dataset': - return Inertia::render('Public/Project/Dataset', [ - 'project' => (new ProjectResource($project))->lite(false, []), + $studyResource = (new StudyResource($study))->lite(false, ['tags', 'sample', 'molecules']); + $datasetResource = (new DatasetResource($dataset))->lite(false, ['nmrium']); + + if ($project) { + return $this->renderPublicProject( + 'Public/Project/Dataset', + [ + 'project' => (new ProjectResource($project))->lite(false, []), + 'tab' => $tab, + 'study' => $studyResource, + 'dataset' => $datasetResource, + ], + $request, + $project, + $getLicense + ); + } + + return Inertia::render('Public/Sample/Dataset', [ 'tab' => $tab, - 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'molecules']), - 'dataset' => (new DatasetResource($dataset)), + 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'molecules', 'owner', 'license', 'authors', 'citations']), + 'dataset' => $datasetResource, ]); - break; default: - return Inertia::render('Public/Project/Show', [ - 'project' => (new ProjectResource($project))->lite(false, ['users', 'authors', 'citations']), - 'tab' => 'info', - ]); + return $this->renderPublicProject( + 'Public/Project/Show', + [ + 'project' => (new ProjectResource($project))->lite(false, ['users', 'authors', 'citations']), + 'tab' => 'info', + ], + $request, + $project, + $getLicense + ); } } else { abort(404, 'Page not found'); } } + /** + * @param array $props + */ + private function renderPublicProject(string $component, array $props, Request $request, ?Project $project, GetLicense $getLicense): InertiaResponse + { + $mergedProps = $props; + if ($project) { + $mergedProps = array_merge( + $props, + ProjectWorkspace::inertiaPropsForPublicProject($request, $project, $getLicense) + ); + } + + return Inertia::render($component, $mergedProps); + } + /** * Resolve the incoming request and render a badge */ diff --git a/app/Http/Controllers/Auth/SocialController.php b/app/Http/Controllers/Auth/SocialController.php index 754f7cbe2..1619de34d 100644 --- a/app/Http/Controllers/Auth/SocialController.php +++ b/app/Http/Controllers/Auth/SocialController.php @@ -7,6 +7,7 @@ use App\Models\Team; use App\Models\User; use Illuminate\Auth\Events\Registered; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Redirect; use Laravel\Socialite\Facades\Socialite; @@ -17,7 +18,7 @@ class SocialController extends Controller /** * Redirect the user to the GitHub authentication page. * - * @return \Illuminate\Http\Response + * @return Response */ public function redirectToProvider($service) { @@ -31,7 +32,7 @@ public function redirectToProvider($service) /** * Obtain the user information from GitHub. * - * @return \Illuminate\Http\Response + * @return Response */ public function handleProviderCallback($service) { diff --git a/app/Http/Controllers/CASController.php b/app/Http/Controllers/CASController.php new file mode 100644 index 000000000..e60b81970 --- /dev/null +++ b/app/Http/Controllers/CASController.php @@ -0,0 +1,45 @@ +validate([ + 'cas_rn' => 'required|string|max:20', + ]); + + $casNumber = $request->input('cas_rn'); + + // Check if API token is configured + if (! Config::get('services.cas.api_token')) { + return response()->json([ + 'error' => 'CAS Service not configured', + ], 500); + } + + try { + $data = $this->casService->getCASDetails($casNumber); + + return response()->json($data); + + } catch (\Exception $e) { + return response()->json([ + 'error' => 'Unable to retrieve CAS details. Please verify the CAS number and try again.', + ], 400); + } + } +} diff --git a/app/Http/Controllers/ChemistryStandardizeController.php b/app/Http/Controllers/ChemistryStandardizeController.php new file mode 100644 index 000000000..e5583ec9c --- /dev/null +++ b/app/Http/Controllers/ChemistryStandardizeController.php @@ -0,0 +1,70 @@ +getContent(); + + if ($mol === '') { + return response()->json([ + 'error' => 'Molecule structure is required.', + ], 422); + } + + $url = config('services.chemistry_standardize.url'); + + if (! is_string($url) || $url === '') { + return response()->json([ + 'error' => 'Chemistry standardize service is not configured.', + ], 500); + } + + $contentType = $request->header('Content-Type', 'application/json'); + + try { + $response = Http::retry(3, 1000) + ->timeout(30) + ->withHeaders([ + 'Content-Type' => $contentType, + 'Accept' => 'application/json', + ]) + ->withBody($mol, $contentType) + ->post($url); + + $response->throw(); + + return response($response->body(), $response->status()) + ->header('Content-Type', $response->header('Content-Type', 'application/json')); + } catch (ConnectionException $e) { + Log::warning('Chemistry standardize API connection failed', ['error' => $e->getMessage()]); + + return response()->json([ + 'error' => 'Unable to reach the chemistry standardize service.', + ], 502); + } catch (RequestException $e) { + Log::warning('Chemistry standardize API request failed', [ + 'error' => $e->getMessage(), + 'status' => $e->response?->status(), + ]); + + $status = $e->response?->status() ?? 502; + + return response($e->response?->body() ?? '', $status) + ->header('Content-Type', $e->response?->header('Content-Type', 'application/json')); + } + } +} diff --git a/app/Http/Controllers/CitationController.php b/app/Http/Controllers/CitationController.php index 80c30ecad..869e09fd1 100644 --- a/app/Http/Controllers/CitationController.php +++ b/app/Http/Controllers/CitationController.php @@ -6,6 +6,7 @@ use App\Actions\Citation\SyncCitations as SyncCitationsAction; use App\Http\Resources\CitationResource; use App\Models\Project; +use App\Models\Study; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -85,6 +86,66 @@ public function destroy(Request $request, Project $project): JsonResponse|Redire } } + /** + * Save and sync updated citation details for a study. + */ + public function saveStudy(Request $request, Study $study): JsonResponse|RedirectResponse + { + if (! Gate::forUser($request->user())->check('updateStudy', $study)) { + return $this->unauthorizedResponse($request, 'You are not authorized to update citations for this study.'); + } + + try { + $request->validate([ + 'citations' => ['required', 'array', 'max:100'], + 'citations.*' => ['required', 'array'], + ]); + + $citations = $request->get('citations', []); + + if (count($citations) > 0) { + $processedCitations = $this->syncCitations->sync($study, $citations, $request->user()); + + return $this->successResponse($request, 'Citation updated successfully', $processedCitations); + } + + return $this->successResponse($request, 'No citations to process'); + } catch (ValidationException $e) { + return $this->validationErrorResponse($request, $e); + } catch (\Exception $e) { + return $this->errorResponse($request, 'An error occurred while updating citations.', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + + /** + * Delete citation for a study. + */ + public function destroyStudy(Request $request, Study $study): JsonResponse|RedirectResponse + { + if (! Gate::forUser($request->user())->check('updateStudy', $study)) { + return $this->unauthorizedResponse($request, 'You are not authorized to remove citations from this study.'); + } + + try { + $request->validate([ + 'citations' => ['required', 'array', 'min:1'], + 'citations.*.id' => ['required', 'integer', 'min:1'], + ]); + + $citations = $request->get('citations'); + + if (count($citations) > 0) { + $this->removeCitation->remove($study, $citations[0]['id']); + } + + return $this->successResponse($request, 'Citation deleted successfully'); + } catch (ValidationException $e) { + return $this->validationErrorResponse($request, $e); + } catch (\Exception $e) { + return $this->errorResponse($request, 'An error occurred while deleting the citation.', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + /** * Return unauthorized response for failed authorization checks. */ diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index daa251c19..29c1e3ddd 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -2,110 +2,286 @@ namespace App\Http\Controllers; +use App\Http\Requests\DashboardIndexRequest; use App\Models\Project; use App\Models\Study; +use App\Models\Team; +use App\Models\User; +use App\Support\Dashboard\CompoundLibraryRankedStudiesQuery; +use App\Support\Dashboard\WorkspaceMoleculeAggregates; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Http\Request; use Inertia\Inertia; class DashboardController extends Controller { - public function dashboard(Request $request) + public function dashboard(DashboardIndexRequest $request) { $user = $request->user(); $team = $user->currentTeam; - $projects = []; - $samples = []; - - if ($team) { - $team->users = $team->allUsers(); - if (! $team->personal_team) { - $projects = Project::with('users', 'owner')->where([['team_id', $team->id], ['is_deleted', false]])->orderBy('updated_at', 'DESC')->get(); - $samples = Study::with('users', 'owner', 'sample.molecules')->where([['project_id', null], ['team_id', $team->id], ['is_deleted', false]])->orderBy('updated_at', 'DESC')->get(); - } else { - $projects = Project::with('users', 'owner')->where([['owner_id', $user->id], ['is_deleted', false]]) - ->where('team_id', $team->id) - ->orderBy('updated_at', 'DESC') - ->get(); - - $samples = Study::with('users', 'owner')->where([['project_id', null], ['owner_id', $user->id], ['is_deleted', false]]) - ->where('team_id', $team->id) - ->orderBy('updated_at', 'DESC')->get(); - } + $filters = $request->dashboardFilters(); + + $emptyProjects = Project::query()->whereRaw('0 = 1')->paginate( + $filters['projects_per_page'], + ['*'], + 'projects_page', + $filters['projects_page'] + )->withQueryString(); + + $emptySamples = Study::query()->whereRaw('0 = 1')->paginate( + $filters['samples_per_page'], + ['*'], + 'samples_page', + $filters['samples_page'] + )->withQueryString(); + + if (! $team) { + return Inertia::render('Dashboard', [ + 'filters' => $filters, + 'team' => null, + 'projects' => $emptyProjects, + 'samples' => $emptySamples, + 'workspaceProjects' => [], + 'workspaceStudies' => [], + 'hasProjects' => false, + 'hasSamples' => false, + 'teamRole' => $user->teamRole($team), + 'user' => $user, + ]); + } + + $team->users = $team->allUsers(); + + $workspace = $filters['workspace'] ?? 'default'; + + if ($workspace !== 'default') { + [$workspaceProjects, $workspaceStudies] = $this->workspaceProjectAndStudyLists($user, $workspace); + + $hasProjects = $this->scopedProjectsQuery($user, $team)->exists(); + $hasSamples = $this->scopedSamplesQuery($user, $team)->exists(); + + return Inertia::render('Dashboard', [ + 'filters' => $filters, + 'team' => $team, + 'projects' => $emptyProjects, + 'samples' => $emptySamples, + 'workspaceProjects' => $workspaceProjects, + 'workspaceStudies' => $workspaceStudies, + 'hasProjects' => $hasProjects, + 'hasSamples' => $hasSamples, + 'teamRole' => $user->teamRole($team), + 'user' => $user, + ]); } + $projectsQuery = $this->scopedProjectsQuery($user, $team) + ->with(['users', 'owner', 'tags', 'draft']) + ->orderByDesc('updated_at'); + + $this->applyStatusFilter($projectsQuery, $filters['projects_status']); + $this->applySearchToProjects($projectsQuery, $filters['projects_q']); + + $projects = $projectsQuery->paginate( + $filters['projects_per_page'], + ['*'], + 'projects_page', + $filters['projects_page'] + )->withQueryString(); + + $samplesQuery = $this->scopedSamplesQuery($user, $team) + ->with([ + 'users', + 'owner', + 'sample.molecules' => function (BelongsToMany $query) use ($user, $team): void { + WorkspaceMoleculeAggregates::applyToMoleculeRelation($query, $user, $team); + }, + ]) + ->orderByDesc('updated_at'); + + $this->applyStudyVisibilityFilter($samplesQuery, $filters['samples_status']); + $this->applySearchToStudies($samplesQuery, $filters['samples_q']); + + $samples = $samplesQuery->paginate( + $filters['samples_per_page'], + ['*'], + 'samples_page', + $filters['samples_page'] + )->withQueryString(); + + $hasProjects = $this->scopedProjectsQuery($user, $team)->exists(); + $hasSamples = $this->scopedSamplesQuery($user, $team)->exists(); + return Inertia::render('Dashboard', [ - 'filters' => $request->all('action', 'draft_id'), + 'filters' => $filters, 'team' => $team, - 'projects' => $projects->load('tags', 'draft'), - 'samples' => $samples->load('sample.molecules'), + 'projects' => $projects, + 'samples' => $samples, + 'workspaceProjects' => [], + 'workspaceStudies' => [], + 'hasProjects' => $hasProjects, + 'hasSamples' => $hasSamples, 'teamRole' => $user->teamRole($team), 'user' => $user, ]); } - public function sharedWithMe(Request $request) + /** + * @return array{0: array, 1: array} + */ + protected function workspaceProjectAndStudyLists(User $user, string $workspace): array { - $user = $request->user(); + return match ($workspace) { + 'shared' => [ + $user->sharedProjects()->with(['owner', 'tags', 'draft'])->get()->all(), + $user->sharedStudies()->with(['owner', 'sample.molecules'])->get()->all(), + ], + 'recent' => [ + $this->recentProjectsForUser($user), + [], + ], + 'starred' => [ + Project::query() + ->where('is_deleted', false) + ->whereHasBookmark($user) + ->with(['owner', 'tags', 'draft']) + ->get() + ->all(), + Study::query() + ->where('is_deleted', false) + ->whereHasBookmark($user) + ->with(['owner', 'sample.molecules']) + ->get() + ->all(), + ], + 'trashed' => [ + Project::query() + ->where('owner_id', $user->id) + ->where('is_deleted', true) + ->with(['owner', 'tags', 'draft']) + ->get() + ->all(), + [], + ], + default => [[], []], + }; + } - $projects = $user->sharedProjects->load('owner'); + /** + * @return array + */ + protected function recentProjectsForUser(User $user): array + { + $projects = $user->activeProjects()->with(['owner', 'tags', 'draft'])->get(); - $studies = $user->sharedStudies->load('owner', 'sample.molecules'); + foreach ($user->allTeams() as $teamModel) { + $projects = $projects->concat( + $teamModel->activeProjects()->with(['owner', 'tags', 'draft'])->get() + ); + } - return Inertia::render('SharedWithMe', [ - 'projects' => $projects, - 'studies' => $studies, - ]); + return $projects->unique('id')->sortByDesc(fn ($project) => $project->updated_at)->values()->all(); } - public function trashed(Request $request) + /** + * @return Builder + */ + protected function scopedProjectsQuery(User $user, Team $team): Builder { - $user = $request->user(); - $projects = Project::where('owner_id', $user->id) - ->Where('is_deleted', true) - ->get(); + $query = Project::query()->where('is_deleted', false); - return Inertia::render('Trashed', [ - 'projects' => $projects, - ]); + if ($team->personal_team) { + $query->where('team_id', $team->id)->where('owner_id', $user->id); + } else { + $query->where('team_id', $team->id); + } + + return $query; } - public function starred(Request $request) + /** + * Studies in the current team (standalone, or under a project that is not in the trash). + * Duplicate compounds (same InChI key / primary molecule) collapse to the study updated most recently. + * + * @return Builder + */ + protected function scopedSamplesQuery(User $user, Team $team): Builder { - $user = $request->user(); - - $projects = Project::where([['is_deleted', false]])->whereHasBookmark( - auth()->user() - )->get(); + return Study::query()->whereIn( + 'studies.id', + function (QueryBuilder $query) use ($user, $team): void { + $query->select('ranked.id') + ->fromSub(CompoundLibraryRankedStudiesQuery::build($user, $team), 'ranked') + ->where('ranked.rn', '=', 1); + } + ); + } - $studies = Study::where([['is_deleted', false]])->whereHasBookmark( - auth()->user() - )->get(); + protected function applyStatusFilter(Builder $query, string $status): void + { + if ($status !== 'all') { + $query->where('status', $status); + } + } - return Inertia::render('Starred', [ - 'projects' => $projects->load('owner'), - 'studies' => $studies, - ]); + /** + * Compound library tab: filter studies by public vs private visibility. + */ + protected function applyStudyVisibilityFilter(Builder $query, string $visibility): void + { + if ($visibility === 'public') { + $query->where('is_public', true); + } elseif ($visibility === 'private') { + $query->where(function (Builder $q): void { + $q->where('is_public', false) + ->orWhereNull('is_public'); + }); + } } - public function recent(Request $request) + protected function applySearchToProjects(Builder $query, string $term): void { - $user = $request->user(); - $team = $user->currentTeam; + $term = trim($term); + if ($term === '') { + return; + } + + $normalized = function_exists('mb_strtolower') + ? mb_strtolower($term) + : strtolower($term); - $projects = $user->activeProjects->load('owner'); + $table = $query->getModel()->getTable(); + $like = '%'.addcslashes($normalized, '%_\\').'%'; - foreach ($user->allTeams() as $team) { - $projects = $projects->concat($team->activeProjects->load('owner')); + $query->where(function (Builder $q) use ($like, $table) { + $q->whereRaw('LOWER('.$table.'.name) LIKE ?', [$like]) + ->orWhereRaw('LOWER('.$table.'.description) LIKE ?', [$like]) + ->orWhereRaw('LOWER(CAST('.$table.'.id AS VARCHAR)) LIKE ?', [$like]) + ->orWhereRaw('LOWER(CAST('.$table.'.uuid AS VARCHAR)) LIKE ?', [$like]); + }); + } + + protected function applySearchToStudies(Builder $query, string $term): void + { + $term = trim($term); + if ($term === '') { + return; } - $projects = $projects->unique()->sortByDesc(function ($project) { - return $project['updated_at']; - })->values(); + $normalized = function_exists('mb_strtolower') + ? mb_strtolower($term) + : strtolower($term); - return Inertia::render('Recent', [ - 'team' => $team, - 'projects' => $projects, - ]); + $table = $query->getModel()->getTable(); + $like = '%'.addcslashes($normalized, '%_\\').'%'; + + $query->where(function (Builder $q) use ($like, $table) { + $q->whereRaw('LOWER('.$table.'.name) LIKE ?', [$like]) + ->orWhereRaw('LOWER('.$table.'.description) LIKE ?', [$like]) + ->orWhereRaw('LOWER(CAST('.$table.'.id AS VARCHAR)) LIKE ?', [$like]) + ->orWhereRaw('LOWER(CAST('.$table.'.uuid AS VARCHAR)) LIKE ?', [$like]); + }); } public function onboardingStatus(Request $request, $status) diff --git a/app/Http/Controllers/DatasetController.php b/app/Http/Controllers/DatasetController.php index 64afcaeaa..ec65a6024 100644 --- a/app/Http/Controllers/DatasetController.php +++ b/app/Http/Controllers/DatasetController.php @@ -5,11 +5,14 @@ use App\Http\Resources\DatasetResource; use App\Models\Dataset; use App\Models\NMRium; +use App\Models\Sample; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; +use Illuminate\Validation\ValidationException; use Inertia\Inertia; class DatasetController extends Controller @@ -19,26 +22,22 @@ public function publicDatasetView(Request $request, $slug) { $dataset = Dataset::where('slug', $slug)->firstOrFail(); - if ($dataset->is_public) { - return Inertia::render('Public/Dataset', [ - 'dataset' => $dataset, - ]); + if (! $dataset->is_public) { + return response()->json([ + 'message' => 'Unauthorized', + ], 401); } + + return Inertia::render('Public/Dataset', [ + 'dataset' => $dataset, + ]); } public function fetchNMRium(Request $request, Dataset $dataset) { - if ($dataset) { - $nmrium = $dataset->nmrium; - if ($nmrium) { - $nmriumInfo = $nmrium->nmrium_info; - $nmriumInfo['data']['molecules'] = []; + $dataset->loadMissing(['study.sample']); - return $nmriumInfo; - } else { - return null; - } - } + return $dataset->normalizedNmriumInfo(); } public function nmriumInfo(Request $request, Dataset $dataset) @@ -52,6 +51,7 @@ public function nmriumInfo(Request $request, Dataset $dataset) $nmriumInfo = sanitizeUnicodeInArray($spectra); $molecularInfo = sanitizeUnicodeInArray($molecules); + $molecularInfo = $this->normalizeMoleculeHeaders($molecularInfo); $nmrium = $dataset->nmrium; if ($nmrium) { @@ -84,14 +84,12 @@ public function nmriumInfo(Request $request, Dataset $dataset) } foreach ($spectra as $spectrum) { - $experimentDetailsExists = array_key_exists('experiment', $spectrum['info']); - if ($experimentDetailsExists) { - $experiment = $spectrum['info']['experiment']; - $nucleus = $spectrum['info']['nucleus']; - if (is_array($nucleus)) { - $nucleus = implode('-', $nucleus); - } - $dataset->type = $experiment.','.$nucleus; + if (! is_array($spectrum)) { + continue; + } + $label = $this->spectrumTypeLabel($spectrum); + if ($label !== null) { + $dataset->type = $label; } } @@ -135,6 +133,63 @@ public function publicDatasetsView(Request $request) ]); } + /** + * Save the user-supplied spectrum assignment block for a dataset. + * + * Authorisation rides on the parent study's `updateStudy` gate so that + * write-access to assignments mirrors the rest of the per-sample + * upload work (avoids inventing a new dataset-level policy for a field + * that conceptually belongs to the same edit session). + * + * Accepts: + * - `acs` (string, optional) free-form ACS-style string + * - `atom_peaks` (array, optional) list of `{atom, peak, label?}` + * - `source` (string, optional) `manual` | `nmrium` (defaults to `manual`) + * + * Pass an empty payload (no `acs`, no `atom_peaks`) to clear the field. + */ + public function updateAssignments(Request $request, Dataset $dataset) + { + $study = $dataset->study; + if (! $study) { + throw ValidationException::withMessages([ + 'dataset' => 'Dataset is not attached to a study.', + ]); + } + Gate::forUser($request->user())->authorize('updateStudy', $study); + + $validated = $request->validate([ + 'acs' => 'nullable|string|max:65535', + 'atom_peaks' => 'nullable|array', + 'atom_peaks.*.atom' => 'nullable|string|max:64', + 'atom_peaks.*.peak' => 'nullable', + 'atom_peaks.*.label' => 'nullable|string|max:255', + 'source' => 'nullable|string|in:manual,nmrium', + ]); + + $acs = isset($validated['acs']) ? trim((string) $validated['acs']) : ''; + $atomPeaks = $validated['atom_peaks'] ?? []; + + if ($acs === '' && empty($atomPeaks)) { + $dataset->assignments = null; + } else { + $dataset->assignments = [ + 'acs' => $acs, + 'atom_peaks' => array_values($atomPeaks), + 'source' => $validated['source'] ?? 'manual', + 'updated_at' => now()->toIso8601String(), + ]; + } + + $dataset->save(); + + return response()->json([ + 'id' => $dataset->id, + 'assignments' => $dataset->assignments, + 'has_assignments' => $dataset->hasAssignments(), + ]); + } + public function snapshot(Request $request, Dataset $dataset) { $content = $request->get('img'); @@ -142,15 +197,171 @@ public function snapshot(Request $request, Dataset $dataset) if ($content) { if ($study->project) { $path = '/projects/'.$study->project->uuid.'/'.$study->uuid.'/'.$dataset->slug.'.svg'; - Storage::disk(env('FILESYSTEM_DRIVER_PUBLIC'))->put($path, $content, 'public'); + Storage::disk(config('filesystems.default_public'))->put($path, $content, 'public'); $dataset->dataset_photo_path = $path; $dataset->save(); } else { $path = '/samples/'.$study->uuid.'/'.$dataset->slug.'.svg'; - Storage::disk(env('FILESYSTEM_DRIVER_PUBLIC'))->put($path, $content, 'public'); + Storage::disk(config('filesystems.default_public'))->put($path, $content, 'public'); $dataset->dataset_photo_path = $path; $dataset->save(); } } } + + /** + * Ensure each molecule entry keeps a valid 3-line MOL header (title, + * generator, comment) before the V2000/V3000 counts line. NMRium and the + * chemistry standardize endpoint occasionally emit molfiles without the + * title line, which silently breaks parsers on subsequent reload. We + * never strip or escape the line; we only prepend blank lines (or the + * molecule's label) when the header has been collapsed below 3 lines. + * See `Sample::ensureMolfileHeader` for details. + * + * @param array|null $molecules + * @return array|null + */ + protected function normalizeMoleculeHeaders($molecules) + { + if (! is_array($molecules)) { + return $molecules; + } + + foreach ($molecules as $idx => $entry) { + if (! is_array($entry)) { + continue; + } + $molfile = $entry['molfile'] ?? null; + if (! is_string($molfile) || $molfile === '') { + continue; + } + $label = isset($entry['label']) ? (string) $entry['label'] : ''; + $molecules[$idx]['molfile'] = Sample::ensureMolfileHeader($molfile, $label); + } + + return $molecules; + } + + /** + * Build a human-readable spectrum type label such as `1H NMR - 1D` from + * an NMRium spectrum payload, falling back to a path-based dimensionality + * guess when the parser-derived `info` block is missing or has been + * corrupted by a legacy save (older builds dropped `experiment`/`nucleus`). + * + * @param array $spectrum + */ + protected function spectrumTypeLabel(array $spectrum): ?string + { + $info = is_array($spectrum['info'] ?? null) ? $spectrum['info'] : []; + + $experiment = isset($info['experiment']) && is_string($info['experiment']) && $info['experiment'] !== '' + ? $this->formatExperimentName($info['experiment']) + : null; + + $nucleus = $info['nucleus'] ?? null; + if (is_array($nucleus)) { + $nucleus = implode('-', array_filter(array_map('strval', $nucleus), fn ($v) => $v !== '')); + } elseif (! is_string($nucleus)) { + $nucleus = null; + } + if ($nucleus === '') { + $nucleus = null; + } + + $dimension = null; + if (isset($info['dimension']) && is_numeric($info['dimension'])) { + $dimension = (int) $info['dimension']; + } else { + $dimension = $this->guessSpectrumDimension($spectrum); + } + + if ($experiment === null && $dimension === null && $nucleus === null) { + return null; + } + + if ($experiment === null && $dimension !== null) { + $experiment = $dimension.'D'; + } + + if ($nucleus !== null && $experiment !== null) { + return $nucleus.' NMR - '.$experiment; + } + + if ($nucleus !== null) { + return $nucleus.' NMR'; + } + + return ($experiment ?? '').' NMR'; + } + + /** + * Render NMRium's lowercase experiment tokens (`hsqc`, `cosy`, `1d`, …) + * in their conventional uppercase form for display. Unknown values are + * passed through unchanged. + */ + protected function formatExperimentName(string $experiment): string + { + $trimmed = trim($experiment); + if ($trimmed === '') { + return $experiment; + } + + $lower = strtolower($trimmed); + + if (preg_match('/^(\d+)d$/', $lower, $m)) { + return $m[1].'D'; + } + + $known = [ + 'cosy', 'noesy', 'roesy', 'tocsy', 'hsqc', 'hmbc', 'hmqc', + 'dept', 'dept45', 'dept90', 'dept135', 'jres', 'inadequate', + 'apt', 'edited-hsqc', 'hsqc-tocsy', + ]; + if (in_array($lower, $known, true)) { + return strtoupper($lower); + } + + return $trimmed; + } + + /** + * Heuristically infer 1D vs 2D from an NMRium spectrum's source selector + * file paths (Bruker conventions: `acqu2s` / `pdata/.../2[ri]+` => 2D, + * `acqus` / `pdata/.../1[ri]` / `fid` => 1D). + * + * @param array $spectrum + */ + protected function guessSpectrumDimension(array $spectrum): ?int + { + $selector = $spectrum['sourceSelector'] ?? $spectrum['selector'] ?? []; + $files = is_array($selector['files'] ?? null) ? $selector['files'] : []; + if (empty($files)) { + return null; + } + + foreach ($files as $file) { + if (! is_string($file)) { + continue; + } + $base = strtolower(basename($file)); + if (in_array($base, ['acqu2s', 'acqu3s', '2rr', '2ri', '2ir', '2ii', '3rrr'], true)) { + return $base === '3rrr' ? 3 : 2; + } + if (preg_match('#/pdata/\d+/2[ri]+$#i', $file)) { + return 2; + } + } + + foreach ($files as $file) { + if (! is_string($file)) { + continue; + } + $base = strtolower(basename($file)); + if (in_array($base, ['acqus', '1r', '1i', 'fid'], true)) { + return 1; + } + } + + return null; + } } diff --git a/app/Http/Controllers/DownloadController.php b/app/Http/Controllers/DownloadController.php index ef3faab47..9c79961ac 100644 --- a/app/Http/Controllers/DownloadController.php +++ b/app/Http/Controllers/DownloadController.php @@ -66,7 +66,7 @@ public function downloadSet(Request $request, $username, $project, $study = null $fsObj = new FileSystemObject; $fsObj->type = 'directory'; $fsObj->name = $project->slug; - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $fsObj->path = $environment.'/'.$project->uuid; $fsObj->key = $project->uuid; $fsObj->relative_url = '/'.$project->uuid; @@ -92,12 +92,12 @@ public function downloadFromProject(Request $request, $username, $project, $key $s3Client = $this->storageClient(); - $bucket = $request->input('bucket') ?: config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.bucket'); + $bucket = $request->input('bucket') ?: config('filesystems.disks.'.config('filesystems.default').'.bucket'); $s3keys = []; if ($fsObj->type == 'file') { - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); if (Storage::has($path)) { array_push($s3keys, substr($fsObj->path, 1)); } @@ -164,19 +164,20 @@ function () use ($s3keys, $bucket, $fsObj) { /** * Get the S3 storage client instance. * - * @return \Aws\S3\S3Client + * @return S3Client */ protected function storageClient() { + $diskName = config('filesystems.default'); $config = [ - 'region' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.region'), + 'region' => config('filesystems.disks.'.$diskName.'.region'), 'version' => 'latest', 'use_path_style_endpoint' => true, - 'url' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.endpoint'), - 'endpoint' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.endpoint'), + 'url' => config('filesystems.disks.'.$diskName.'.endpoint'), + 'endpoint' => config('filesystems.disks.'.$diskName.'.endpoint'), 'credentials' => [ - 'key' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.key'), - 'secret' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.secret'), + 'key' => config('filesystems.disks.'.$diskName.'.key'), + 'secret' => config('filesystems.disks.'.$diskName.'.secret'), ], ]; diff --git a/app/Http/Controllers/DraftController.php b/app/Http/Controllers/DraftController.php index eb9e2fc8a..8fddc5fd1 100644 --- a/app/Http/Controllers/DraftController.php +++ b/app/Http/Controllers/DraftController.php @@ -4,14 +4,21 @@ use App\Actions\Draft\DraftFiles; use App\Actions\Draft\ProcessDraft; +use App\Actions\Draft\ResetSampleFolder; use App\Actions\Draft\UserDrafts; use App\Jobs\ProcessFiles; use App\Models\Draft; use App\Models\FileSystemObject; use App\Models\Project; +use App\Models\User; +use App\Support\ProvisionalDoi; use Illuminate\Http\JsonResponse; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; +use RuntimeException; /** * Handle draft management operations including file processing, validation, and project conversion. @@ -28,7 +35,8 @@ public function __construct( private FileSystemController $fileSystemController, private UserDrafts $userDrafts, private ProcessDraft $processDraft, - private DraftFiles $draftFiles + private DraftFiles $draftFiles, + private ResetSampleFolder $resetSampleFolder ) {} /** @@ -52,7 +60,7 @@ public function all(Request $request): JsonResponse /** * Process draft and convert to project structure. * - * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse + * @return Response|JsonResponse|RedirectResponse */ public function process(Request $request, Draft $draft) { @@ -81,6 +89,50 @@ public function missingFiles(Request $request, Draft $draft): JsonResponse return response()->json($missingFilesData); } + /** + * Reset cached state for a single sample folder so the next "Proceed to + * Step 2" run reprocesses it from scratch. + */ + public function resetSampleFolder(Request $request, Draft $draft, FileSystemObject $filesystemobject): JsonResponse + { + /** @var User $user */ + $user = Auth::user(); + [$user_id] = $user->getUserTeamData(); + + if ($draft->owner_id !== $user_id) { + abort(403); + } + + if ($filesystemobject->draft_id !== $draft->id) { + return response()->json([ + 'ok' => false, + 'message' => 'Filesystem object does not belong to this draft.', + ], 403); + } + + $result = $this->resetSampleFolder->execute($draft, $filesystemobject); + + return response()->json($result, $result['ok'] ? 200 : 422); + } + + /** + * Get a single draft by ID with ownership verification. + */ + public function show(Request $request, Draft $draft): JsonResponse + { + /** @var User $user */ + $user = Auth::user(); + [$user_id] = $user->getUserTeamData(); + + if ($draft->owner_id !== $user_id) { + abort(403); + } + + return response()->json([ + 'draft' => $draft->load(['Tags', 'project:id,slug,status,draft_id']), + ]); + } + /** * Update draft properties. */ @@ -124,14 +176,137 @@ public function info(Request $request, Draft $draft): JsonResponse { $project = Project::where('draft_id', $draft->id)->first(); - $studies = json_decode($project->studies->load(['datasets', 'sample.molecules', 'tags'])); + if (! $project) { + return response()->json([ + 'project' => null, + 'studies' => [], + ]); + } + + $project->load(['owner']); + $studies = $project->studies() + ->with(['datasets', 'sample.molecules', 'tags']) + ->get(); + + return response()->json([ + 'project' => $project, + 'studies' => $studies, + ]); + } + + /** + * Lightweight study processing status for upload polling (read-only). + */ + public function status(Request $request, Draft $draft): JsonResponse + { + /** @var User $user */ + $user = Auth::user(); + [$user_id] = $user->getUserTeamData(); + + if ($draft->owner_id !== $user_id) { + abort(403); + } + + $project = Project::where('draft_id', $draft->id)->first(); + + if (! $project) { + return response()->json([ + 'project_id' => null, + 'inprogress_count' => 0, + 'studies' => [], + ]); + } + + $studies = $project->studies() + ->select(['id', 'name', 'slug', 'internal_status', 'has_nmrium', 'project_id']) + ->orderBy('name') + ->get(); return response()->json([ - 'project' => $project->load(['owner']), + 'project_id' => $project->id, + 'inprogress_count' => $studies->where('internal_status', '!=', 'complete')->count(), 'studies' => $studies, ]); } + /** + * Create or return the draft project's provisional DOI (not registered with DataCite). + */ + public function storeProvisionalDoi(Request $request, Draft $draft): JsonResponse + { + $this->authorizeDraftOwner($draft); + + /** @var User $user */ + $user = Auth::user(); + [$user_id, $team_id, $team] = $user->getUserTeamData(); + + try { + $payload = DB::transaction(function () use ($draft, $user, $user_id, $team_id, $team): array { + Draft::query()->whereKey($draft->id)->lockForUpdate()->firstOrFail(); + + $project = Project::query()->where('draft_id', $draft->id)->first(); + + if (! $project) { + $project = $this->processDraft->createNewProject($draft, $user_id, $team_id, $user, $team); + } + + $locked = Project::query()->whereKey($project->id)->lockForUpdate()->firstOrFail(); + + if ($locked->provisional_doi === null || $locked->provisional_doi === '') { + $locked->provisional_doi = ProvisionalDoi::forDraft($draft); + $locked->save(); + } + + $locked->refresh(); + + return [ + 'provisional_doi' => $locked->provisional_doi, + 'url' => $locked->provisional_doi_url, + ]; + }); + } catch (RuntimeException $e) { + return response()->json([ + 'message' => $e->getMessage(), + ], 503); + } + + return response()->json($payload); + } + + /** + * Clear the provisional DOI for the draft's project. + */ + public function destroyProvisionalDoi(Request $request, Draft $draft): Response + { + $this->authorizeDraftOwner($draft); + + $project = Project::query()->where('draft_id', $draft->id)->first(); + + if (! $project) { + return response()->noContent(); + } + + if ($project->draft_id === null) { + abort(403); + } + + $project->provisional_doi = null; + $project->save(); + + return response()->noContent(); + } + + private function authorizeDraftOwner(Draft $draft): void + { + /** @var User $user */ + $user = Auth::user(); + [$user_id] = $user->getUserTeamData(); + + if ($draft->owner_id !== $user_id) { + abort(403); + } + } + /** * Trigger file annotation processing for draft folders. */ diff --git a/app/Http/Controllers/FileSystemController.php b/app/Http/Controllers/FileSystemController.php index 7a9857cad..1503bf9bc 100644 --- a/app/Http/Controllers/FileSystemController.php +++ b/app/Http/Controllers/FileSystemController.php @@ -9,10 +9,12 @@ use App\Services\ELNMetadataServiceFactory; use App\Services\FileSystemObjectService; use App\Services\StorageSignedUrlService; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\ValidationException; /** * Handle file system operations and signed URL generation for file uploads. @@ -30,8 +32,8 @@ public function __construct( /** * Generate signed URLs for draft file uploads. * - * @throws \Illuminate\Validation\ValidationException - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @throws ValidationException + * @throws ModelNotFoundException */ public function signedDraftStorageURL(Request $request): JsonResponse { @@ -71,8 +73,8 @@ public function signedDraftStorageURL(Request $request): JsonResponse /** * Generate signed URLs for project file uploads. * - * @throws \Illuminate\Validation\ValidationException - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @throws ValidationException + * @throws ModelNotFoundException */ public function signedStorageURL(Request $request): JsonResponse { @@ -108,7 +110,7 @@ public function signedStorageURL(Request $request): JsonResponse /** * Delete a filesystem object and all its children recursively. * - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @throws ModelNotFoundException */ public function deleteFSO(Request $request, Draft $draft, FileSystemObject $filesystemobject): JsonResponse { diff --git a/app/Http/Controllers/OrcidController.php b/app/Http/Controllers/OrcidController.php new file mode 100644 index 000000000..3af51001d --- /dev/null +++ b/app/Http/Controllers/OrcidController.php @@ -0,0 +1,86 @@ +input('q'); + + if (! $query) { + return response()->json(['error' => 'Query parameter is required'], 400); + } + + try { + $response = Http::withHeaders([ + 'Accept' => 'application/json', + ])->get(config('orcid.base_url').'/search', [ + 'q' => $query, + ]); + + if ($response->failed()) { + return response()->json([ + 'error' => 'Failed to fetch ORCID search results', + 'message' => $response->json('error-desc.value') ?? 'ORCID API returned an error', + ], $response->status()); + } + + return response()->json($response->json()); + } catch (\Exception $e) { + return response()->json(['error' => 'Failed to fetch ORCID search results'], 500); + } + } + + public function person(string $orcidId): JsonResponse + { + if (! $orcidId) { + return response()->json(['error' => 'ORCID ID is required'], 400); + } + + try { + $response = Http::withHeaders([ + 'Accept' => 'application/json', + ])->get(config('orcid.base_url').'/'.$orcidId.'/person'); + + if ($response->failed()) { + return response()->json([ + 'error' => 'Failed to fetch person data', + 'message' => $response->json('error-desc.value') ?? 'ORCID API returned an error', + ], $response->status()); + } + + return response()->json($response->json()); + } catch (\Exception $e) { + return response()->json(['error' => 'Failed to fetch person data'], 500); + } + } + + public function employment(string $orcidId): JsonResponse + { + if (! $orcidId) { + return response()->json(['error' => 'ORCID ID is required'], 400); + } + + try { + $response = Http::withHeaders([ + 'Accept' => 'application/json', + ])->get(config('orcid.base_url').'/'.$orcidId.'/employments'); + + if ($response->failed()) { + return response()->json([ + 'error' => 'Failed to fetch employment data', + 'message' => $response->json('error-desc.value') ?? 'ORCID API returned an error', + ], $response->status()); + } + + return response()->json($response->json()); + } catch (\Exception $e) { + return response()->json(['error' => 'Failed to fetch employment data'], 500); + } + } +} diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 58f1f9481..314c06397 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -6,9 +6,11 @@ use App\Actions\Project\ArchiveProject; use App\Actions\Project\CreateNewProject; use App\Actions\Project\DeleteProject; +use App\Actions\Project\PublishEmbargoProject; use App\Actions\Project\PublishProject; use App\Actions\Project\RestoreProject; use App\Actions\Project\UpdateProject; +use App\Exceptions\EmbargoPublicationFailed; use App\Http\Resources\ProjectResource; use App\Http\Resources\StudyResource; use App\Jobs\ProcessSubmission; @@ -16,12 +18,16 @@ use App\Models\Study; use App\Models\User; use App\Models\Validation; -use Auth; +use App\Support\ProjectWorkspace; +use Carbon\Carbon; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Http\JsonResponse; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\Http\Resources\Json\AnonymousResourceCollection; use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; use Inertia\Inertia; use Laravel\Fortify\Actions\ConfirmPassword; @@ -31,7 +37,7 @@ class ProjectController extends Controller { - public function publicProjectView(Request $request, $owner, $slug) + public function publicProjectView(Request $request, $owner, $slug, GetLicense $getLicense) { $user = User::where('username', $owner)->firstOrFail(); @@ -43,42 +49,49 @@ public function publicProjectView(Request $request, $owner, $slug) } } + $render = fn (string $component, array $props) => Inertia::render($component, array_merge( + $props, + ProjectWorkspace::inertiaPropsForPublicProject($request, $project, $getLicense) + )); + $tab = $request->get('tab'); if ($tab == 'info') { - return Inertia::render('Public/Project/Show', [ + return $render('Public/Project/Show', [ 'project' => (new ProjectResource($project))->lite(false, ['users', 'authors', 'citations']), 'tab' => $tab, ]); - } elseif ($tab == 'samples') { - return Inertia::render('Public/Project/Studies', [ - 'project' => (new ProjectResource($project))->lite(false, ['studies']), + } + if ($tab == 'samples') { + return $render('Public/Project/Samples', [ + 'project' => (new ProjectResource($project))->lite(false, []), 'tab' => $tab, ]); - } elseif ($tab == 'files') { - return Inertia::render('Public/Project/Files', [ + } + if ($tab == 'files') { + return $render('Public/Project/Files', [ 'project' => (new ProjectResource($project))->lite(false, ['files']), 'tab' => $tab, ]); - } elseif ($tab == 'license') { - return Inertia::render('Public/Project/License', [ - 'project' => (new ProjectResource($project))->lite(false, ['license']), - 'tab' => $tab, - ]); - } elseif ($tab == 'study') { + } + if ($tab == 'study') { $studyId = $request->get('id'); - $study = Study::where([['slug', $studyId], ['owner_id', $user->id], ['project_id', $project->id]])->firstOrFail(); + $study = Study::with('linkedCitations')->where([ + ['slug', $studyId], + ['owner_id', $user->id], + ['project_id', $project->id], + ])->firstOrFail(); - return Inertia::render('Public/Project/Study', [ + return $render('Public/Project/Study', [ 'project' => (new ProjectResource($project))->lite(false, []), 'tab' => $tab, - 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'datasets', 'molecules']), - ]); - } else { - return Inertia::render('Public/Project/Show', [ - 'project' => (new ProjectResource($project))->lite(false, ['users', 'authors', 'citations']), - 'tab' => 'info', + 'study' => (new StudyResource($study))->lite(false, ['tags', 'sample', 'datasets', 'molecules', 'citations']), ]); } + + return $render('Public/Project/Show', [ + 'project' => (new ProjectResource($project))->lite(false, ['users', 'authors', 'citations']), + 'tab' => 'info', + ]); } public function publicProjectsView(Request $request) @@ -93,7 +106,7 @@ public function publicProjectsView(Request $request) public function publicStudies(Request $request, Project $project) { - return StudyResource::collection(Study::where([['project_id', $project->id], ['is_public', true]])->filter($request->only('search', 'sort', 'mode'))->paginate(12)->withQueryString()); + return $this->projectStudiesResponse($request, $project, publicOnly: true); } public function toggleUpVote(Request $request, Project $project) @@ -119,27 +132,26 @@ public function show(Request $request, Project $project, GetLicense $getLicense) throw new AuthorizationException; } - $team = $project->nonPersonalTeam; - $user = Auth::user(); - $license = null; - if ($project->license_id) { - $license = $getLicense->getLicensebyId($project->license_id); + $rawIdentifier = $project->getRawOriginal('identifier'); + if ($rawIdentifier !== null && $rawIdentifier !== '') { + $targetUrl = route('public.project.id', ['id' => 'P'.$rawIdentifier]); + $query = array_intersect_key( + $request->query(), + array_flip(['edit', 'tab']) + ); + if ($query !== []) { + $targetUrl .= '?'.http_build_query($query); + } + + return redirect()->to($targetUrl); } - return Inertia::render('Project/Show', [ - 'project' => $project->load('projectInvitations', 'tags', 'authors', 'citations', 'owner'), - 'team' => $team ? $team->load(['users', 'owner']) : null, - 'members' => $project->allUsers(), - 'availableRoles' => array_values(Jetstream::$roles), - 'role' => $project->userProjectRole($user->email), - 'teamRole' => $user->belongsToTeam($team) ? $user->teamRole($team) : null, - 'license' => $license, - 'projectPermissions' => [ - 'canDeleteProject' => Gate::check('deleteProject', $project), - 'canUpdateProject' => Gate::check('updateProject', $project), - 'canManageSettings' => Gate::check('manageSettings', $project), + return Inertia::render('Project/Show', array_merge( + [ + 'project' => $project->load('projectInvitations', 'tags', 'authors', 'citations', 'owner'), ], - ]); + ProjectWorkspace::dashboardShowCompanionProps($request, $project, $getLicense) + )); } public function review(Request $request, $obfuscationCode, GetLicense $getLicense) @@ -188,7 +200,39 @@ public function studies(Request $request, Project $project) throw new AuthorizationException; } - return StudyResource::collection(Study::where('project_id', $project->id)->filter($request->only('search', 'sort', 'mode'))->paginate(9)->withQueryString()); + return $this->projectStudiesResponse($request, $project, publicOnly: false); + } + + /** + * @return AnonymousResourceCollection + */ + protected function projectStudiesResponse(Request $request, Project $project, bool $publicOnly) + { + $query = Study::query()->where('project_id', $project->id); + + if ($publicOnly) { + $query->where('is_public', true); + } + + $query->filter($request->only('search', 'sort', 'mode')); + + if ($request->boolean('for_nav')) { + $query->with(['datasets' => fn ($datasetQuery) => $datasetQuery->orderBy('name')]); + } + + $perPage = $request->boolean('for_nav') + ? min($request->integer('per_page', 100), 100) + : ($publicOnly ? 12 : 9); + + $paginator = $query->paginate($perPage)->withQueryString(); + + if ($request->boolean('for_nav')) { + $paginator->getCollection()->transform( + fn (Study $study) => (new StudyResource($study))->lite(false, ['datasets']) + ); + } + + return StudyResource::collection($paginator); } public function settings(Request $request, Project $project) @@ -199,7 +243,7 @@ public function settings(Request $request, Project $project) return Inertia::render('Project/Settings', [ 'project' => $project, - 'schema' => $environment = env('SCHEMA_VERSION', 'local'), + 'schema' => config('app.schema_version', 'beta'), 'projectPermissions' => [ 'canDeleteProject' => Gate::check('deleteProject', $project), ], @@ -251,7 +295,7 @@ public function toggleArchive(Request $request, StatefulGuard $guard, Project $p ]); } - $creator->toggle($project); + $creator->toggleArchive($project); return redirect()->route('dashboard')->with('success', 'Project archive state updated successfully'); } @@ -324,43 +368,119 @@ public function validationReport(Request $request, Project $project) public function publish(Request $request, Project $project, PublishProject $publisher, UpdateProject $updater) { + if (! Gate::forUser($request->user())->allows('publishProject', $project)) { + if ($this->publishPrefersJsonResponse($request)) { + return response()->json(['message' => 'Forbidden'], 403); + } + + throw new AuthorizationException; + } + if ($project) { - $input = $request->all(); - $release_date = $input['release_date']; $enableProjectMode = $request->get('enableProjectMode'); if ($enableProjectMode) { $validation = $project->validation; + if (! $validation) { + if ($this->publishPrefersJsonResponse($request)) { + return response()->json([ + 'errors' => 'Project validation not found. Please ensure the project is properly configured.', + ], 422); + } + + throw ValidationException::withMessages([ + 'publish' => 'Project validation not found. Please ensure the project is properly configured.', + ]); + } + + $project->release_date = $request->get('release_date'); $validation->process(); $validation = $validation->fresh(); if ($validation['report']['project']['status']) { - $project->release_date = $request->get('release_date'); $project->status = 'queued'; $project->save(); + Log::info('embargo_publish_trace', [ + 'stage' => 'publish_controller_dispatch_process_submission', + 'branch' => 'enable_project_mode', + 'project_id' => $project->id, + 'release_date' => $this->formatReleaseDateForLog($project->release_date), + 'status' => $project->status, + ]); + ProcessSubmission::dispatch($project); - return response()->json([ - 'project' => $project, - 'validation' => $validation, - ]); + if ($this->publishPrefersJsonResponse($request)) { + return response()->json([ + 'project' => $project, + 'validation' => $validation, + ]); + } + + return $this->redirectToProjectCanonicalHome($project) + ->with('success', 'Your submission has been queued for processing.'); } else { - return response()->json([ - 'errors' => 'Validation failing. Please provide all the required data and try again. If the problem persists, please contact us.', - 'validation' => $validation, - ], 422); + $project->refresh(); + + if ($this->publishPrefersJsonResponse($request)) { + return response()->json([ + 'errors' => 'Validation failing. Please provide all the required data and try again. If the problem persists, please contact us at info.nmrxiv@uni-jena.de', + 'validation' => $validation, + ], 422); + } + + session()->now( + 'publish_validation_hints', + $this->publishValidationHintsFromReport($validation->report) + ); + + throw ValidationException::withMessages([ + 'publish' => 'Validation failing. Please provide all the required data and try again. If the problem persists, please contact us at info.nmrxiv@uni-jena.de', + ]); } } else { $draft = $project->draft; - $draft->project_enabled = false; - $draft->save(); + if ($draft) { + $draft->project_enabled = false; + $draft->save(); + } - $project->release_date = $request->get('release_date'); - $project->status = 'queued'; - $project->save(); + // Sample collection mode: always immediate public release (no embargo). + $project->release_date = now()->startOfDay()->toDateString(); + + $project->load('draft'); $validation = $project->validation; - $validation->process(); - $validation = $validation->fresh(); + if ($validation) { + $validation->process(forceSamplesMode: true); + $validation = $validation->fresh(); + } + + $status = true; + if ($validation) { + $status = Validation::samplesModePublishPasses($validation->report); + } + + if (! $status) { + $project->refresh(); + + $message = 'Validation failing. Please provide all the required data and try again. If the problem persists, please contact us at info.nmrxiv@uni-jena.de'; + + if ($this->publishPrefersJsonResponse($request)) { + return response()->json([ + 'errors' => $message, + 'validation' => $validation, + ], 422); + } + + session()->now( + 'publish_validation_hints', + $this->publishValidationHintsFromReport($validation?->report) + ); + + throw ValidationException::withMessages([ + 'publish' => $message, + ]); + } foreach ($project->studies as $study) { $study->license_id = $project->license_id; @@ -371,34 +491,116 @@ public function publish(Request $request, Project $project, PublishProject $publ } } - $status = true; + $project->status = 'queued'; + $project->save(); - foreach ($validation['report']['project']['studies'] as $study) { - if (! $study['status']) { - $status = false; - } - } - // add license check - if ($status) { - ProcessSubmission::dispatch($project); + Log::info('embargo_publish_trace', [ + 'stage' => 'publish_controller_dispatch_process_submission', + 'branch' => 'default_samples_mode', + 'project_id' => $project->id, + 'release_date' => $this->formatReleaseDateForLog($project->release_date), + 'status' => $project->status, + ]); + + ProcessSubmission::dispatch($project); + if ($this->publishPrefersJsonResponse($request)) { return response()->json([ 'project' => $project, 'validation' => $validation, ]); - } else { - return response()->json([ - 'errors' => 'Validation failing. Please provide all the required data and try again. If the problem persists, please contact us.', - ], 422); } + + return $this->redirectToProjectCanonicalHome($project) + ->with('success', 'Your submission has been queued for processing.'); } } } + /** + * Prefer the stable public URL when the project already has an assigned identifier. + */ + protected function redirectToProjectCanonicalHome(Project $project): RedirectResponse + { + $rawIdentifier = $project->getRawOriginal('identifier'); + if ($rawIdentifier !== null && $rawIdentifier !== '') { + return redirect()->route('public.project.id', ['id' => 'P'.$rawIdentifier]); + } + + return redirect()->route('dashboard.projects', $project); + } + + /** + * @param array|null $report + * @return array + */ + protected function publishValidationHintsFromReport(?array $report): array + { + if ($report === null || ! isset($report['project']) || ! is_array($report['project'])) { + return []; + } + + $hints = []; + $project = $report['project']; + + $citations = $project['citations'] ?? null; + if (is_string($citations) && str_starts_with($citations, 'false|')) { + $hints[] = 'Add a DOI to every citation, or choose a future release date if you are not ready to publish immediately.'; + } + + $labels = [ + 'title' => 'project name', + 'description' => 'description', + 'authors' => 'authors', + 'license' => 'license', + 'keywords' => 'keywords', + ]; + + foreach ($labels as $field => $label) { + $value = $project[$field] ?? null; + if (is_string($value) && str_starts_with($value, 'false|')) { + $hints[] = 'Complete the '.$label.' on the project before publishing.'; + } + } + + if (isset($project['studies']) && is_array($project['studies'])) { + foreach ($project['studies'] as $study) { + if (isset($study['status']) && $study['status'] === false) { + $hints[] = 'One or more samples or datasets are incomplete. Open each sample and check metadata, structure, and spectral data.'; + + break; + } + } + } + + if ($hints === [] && isset($project['status']) && $project['status'] === false) { + $hints[] = 'Review project metadata and samples, then try again. You can also open the publish wizard for a full validation view.'; + } + + return array_values(array_unique($hints)); + } + + protected function publishPrefersJsonResponse(Request $request): bool + { + return ! $request->header('X-Inertia'); + } + + /** + * @param Carbon|string|null $value + */ + protected function formatReleaseDateForLog(mixed $value): ?string + { + if ($value === null || $value === '') { + return null; + } + + return Carbon::parse($value)->toIso8601String(); + } + public function store(Request $request, CreateNewProject $creator) { - if (! Gate::forUser($request->user())->check('createProject', $project)) { + if (! Gate::forUser($request->user())->allows('createProject', Project::class)) { throw new AuthorizationException; } @@ -415,16 +617,78 @@ public function update(Request $request, UpdateProject $updater, Project $projec $updater->update($project, $request->all()); - return $request->wantsJson() ? new JsonResponse('', 200) : back()->with('success', 'Project updated successfully'); + if ($request->wantsJson()) { + return new JsonResponse('', 200); + } + + $redirect = back(); + + if (! $request->boolean('suppress_project_updated_flash')) { + $redirect = $redirect->with('success', 'Project updated successfully'); + } + + return $redirect; } public function updateReleaseDate(Request $request, UpdateProject $updater, Project $project) { + if (! Gate::forUser($request->user())->check('updateProject', $project)) { + throw new AuthorizationException; + } + $updater->update($project, $request->all()); return $request->wantsJson() ? new JsonResponse('', 200) : back()->with('success', "Project's release date updated successfully"); } + public function publishEmbargoProject(Request $request, Project $project, PublishEmbargoProject $embargoPublisher): JsonResponse|RedirectResponse + { + if (! Gate::forUser($request->user())->allows('publishProject', $project)) { + if ($this->publishPrefersJsonResponse($request)) { + return response()->json(['message' => 'Forbidden'], 403); + } + + throw new AuthorizationException; + } + + try { + $result = $embargoPublisher->publish($project, restoreReleaseDateOnValidationFailure: true); + } catch (EmbargoPublicationFailed $e) { + if ($this->publishPrefersJsonResponse($request)) { + return response()->json([ + 'errors' => $e->getMessage(), + 'validation' => $e->validation, + ], 422); + } + + session()->now( + 'publish_validation_hints', + $this->publishValidationHintsFromReport($e->validation?->report) + ); + + throw ValidationException::withMessages([ + 'publish' => $e->getMessage(), + ]); + } catch (ValidationException $e) { + if ($this->publishPrefersJsonResponse($request)) { + return response()->json([ + 'errors' => $e->errors()['publish'][0] ?? $e->getMessage(), + ], 422); + } + + throw $e; + } + + return $this->publishPrefersJsonResponse($request) + ? response()->json([ + 'project' => $project->fresh(), + 'validation' => $result['validation'], + ]) + : back()->with('success', $result['dispatched'] === 'async' + ? 'Your submission has been queued for processing.' + : 'Project published successfully'); + } + public function destroy(Request $request, StatefulGuard $guard, Project $project, DeleteProject $creator) { if (! Gate::forUser($request->user())->check('deleteProject', $project)) { @@ -458,15 +722,19 @@ public function destroy(Request $request, StatefulGuard $guard, Project $project */ public function prepareSendList($project) { - $sendTo = []; - foreach ($project->allUsers() as $member) { - if ($member->projectMembership->role == 'creator' || $member->projectMembership->role == 'owner') { - array_push($sendTo, $member); - } else { - array_push($sendTo, $project->owner); + $sendTo = collect(); + + if ($project->owner) { + $sendTo->push($project->owner); + } + + foreach ($project->users as $member) { + $role = $member->projectMembership?->role; + if ($role === 'creator' || $role === 'owner') { + $sendTo->push($member); } } - return $sendTo; + return $sendTo->unique('id')->values()->all(); } } diff --git a/app/Http/Controllers/ProjectInvitationController.php b/app/Http/Controllers/ProjectInvitationController.php index 28dc8adfc..37b157c06 100644 --- a/app/Http/Controllers/ProjectInvitationController.php +++ b/app/Http/Controllers/ProjectInvitationController.php @@ -5,6 +5,7 @@ use App\Actions\Project\AddProjectMember; use App\Models\ProjectInvitation; use Illuminate\Auth\Access\AuthorizationException; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Gate; @@ -14,7 +15,7 @@ class ProjectInvitationController extends Controller * Accept a project invitation. * * @param \Laravel\Jetstream\ProjectInvitation $invitation - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function acceptInvitation(Request $request, ProjectInvitation $invitation) { @@ -36,7 +37,7 @@ public function acceptInvitation(Request $request, ProjectInvitation $invitation * Cancel the given project invitation. * * @param \Laravel\Jetstream\ProjectInvitation $invitation - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function destroyInvitation(Request $request, ProjectInvitation $invitation) { diff --git a/app/Http/Controllers/ProjectMemberController.php b/app/Http/Controllers/ProjectMemberController.php index a347c4148..a81106b2d 100644 --- a/app/Http/Controllers/ProjectMemberController.php +++ b/app/Http/Controllers/ProjectMemberController.php @@ -7,6 +7,7 @@ use App\Actions\Project\UpdateProjectMemberRole; use App\Models\Project; use App\Models\User; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class ProjectMemberController extends Controller @@ -15,7 +16,7 @@ class ProjectMemberController extends Controller * Add a new team member to a project. * * @param int $projectId - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function memberStore(Request $request, $projectId) { @@ -37,7 +38,7 @@ public function memberStore(Request $request, $projectId) * * @param int $projectId * @param int $userId - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function updateMemberRole(Request $request, $projectId, $userId) { @@ -56,7 +57,7 @@ public function updateMemberRole(Request $request, $projectId, $userId) * * @param int $projectId * @param int $userId - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function removeMember(Request $request, $projectId, $userId) { diff --git a/app/Http/Controllers/RorController.php b/app/Http/Controllers/RorController.php new file mode 100644 index 000000000..74d2b6133 --- /dev/null +++ b/app/Http/Controllers/RorController.php @@ -0,0 +1,63 @@ +all(), [ + 'query' => 'required|string|min:3|max:255', + ]); + + if ($validator->fails()) { + return response()->json([ + 'error' => 'Invalid search query', + 'messages' => $validator->errors(), + ], 422); + } + + $query = $request->input('query'); + + try { + $response = Http::timeout(10) + ->acceptJson() + ->get(config('ror.api_url', 'https://api.ror.org/organizations'), [ + 'query' => $query, + ]); + + if ($response->successful()) { + return response()->json($response->json()); + } + + Log::warning('ROR API request failed', [ + 'status' => $response->status(), + 'body' => $response->body(), + ]); + + return response()->json([ + 'error' => 'Failed to fetch organizations', + 'items' => [], + ], $response->status()); + } catch (\Exception $e) { + Log::error('ROR API exception', [ + 'message' => $e->getMessage(), + 'query' => $query, + ]); + + return response()->json([ + 'error' => 'An error occurred while searching for organizations', + 'items' => [], + ], 500); + } + } +} diff --git a/app/Http/Controllers/StudyController.php b/app/Http/Controllers/StudyController.php index eb538299f..ccd4f1620 100644 --- a/app/Http/Controllers/StudyController.php +++ b/app/Http/Controllers/StudyController.php @@ -5,6 +5,7 @@ use App\Actions\License\GetLicense; use App\Actions\Study\CreateNewStudy; use App\Actions\Study\UpdateStudy; +use App\Http\Controllers\API\Schemas\Bioschemas\BioschemasHelper; use App\Http\Resources\StudyResource; use App\Models\FileSystemObject; use App\Models\Molecule; @@ -13,12 +14,13 @@ use App\Models\Sample; use App\Models\Study; use App\Models\User; -use Illuminate\Auth\Access\AuthorizationException; +use App\Support\Nmr\JcampDatasetClassifier; use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; @@ -65,6 +67,8 @@ public function store(Request $request, CreateNewStudy $creator) public function update(Request $request, UpdateStudy $updater, Study $study) { + Gate::authorize('updateStudy', $study); + $updater->update($study, $request->all()); $study = $study->fresh(); @@ -78,12 +82,10 @@ public function update(Request $request, UpdateStudy $updater, Study $study) public function show(Request $request, Study $study, GetLicense $getLicense) { - if (! Gate::forUser($request->user())->check('viewStudy', $study)) { - throw new AuthorizationException; - } + Gate::forUser($request->user())->authorize('viewStudy', $study); $project = $study->project; - $team = $project->nonPersonalTeam; + $team = $project?->nonPersonalTeam; $license = null; if ($study->license_id) { $license = $getLicense->getLicensebyId($study->license_id); @@ -102,12 +104,10 @@ public function protocols(Request $request, Study $study) public function datasets(Request $request, Study $study) { - if (! Gate::forUser($request->user())->check('viewStudy', $study)) { - throw new AuthorizationException; - } + Gate::forUser($request->user())->authorize('viewStudy', $study); $project = $study->project; - $team = $project->team; + $team = $project?->team; return $this->renderTabView('Datasets', $study, $team, $project, null, null, false); } @@ -117,7 +117,7 @@ public function preview2(Request $request, $obfuscationCode, Study $study, $mode switch ($model) { case 'study': $project = Project::where([['is_archived', false], ['obfuscationcode', $obfuscationCode]])->firstOrFail(); - $team = $project->nonPersonalTeam; + $team = $project?->nonPersonalTeam; $license = null; if ($study->license_id) { $license = $getLicense->getLicensebyId($study->license_id); @@ -128,7 +128,7 @@ public function preview2(Request $request, $obfuscationCode, Study $study, $mode break; case 'files': $project = Project::where([['is_archived', false], ['obfuscationcode', $obfuscationCode]])->firstOrFail(); - $team = $project->nonPersonalTeam; + $team = $project?->nonPersonalTeam; $studyFSObject = $study->fsObject; return $this->renderTabView('Files', $study, $team, $project, null, $studyFSObject, true); @@ -136,7 +136,7 @@ public function preview2(Request $request, $obfuscationCode, Study $study, $mode break; case 'datasets': $project = Project::where([['is_archived', false], ['obfuscationcode', $obfuscationCode]])->firstOrFail(); - $team = $project->nonPersonalTeam; + $team = $project?->nonPersonalTeam; return $this->renderTabView('Datasets', $study, $team, $project, null, null, true); @@ -149,9 +149,9 @@ public function renderTabView($tab, $study, $team, $project, $license, $studyFSO switch ($tab) { case 'About': return Inertia::render('Study/About', [ - 'study' => $study->load('users', 'owner', 'studyInvitations', 'tags', 'sample.molecules'), + 'study' => $study->load('users', 'owner', 'studyInvitations', 'tags', 'sample.molecules', 'studyAuthors'), 'team' => $team ? $team->load('users', 'owner') : null, - 'project' => $project ? $project->load('users', 'owner') : null, + 'project' => $project ? $project->load('users', 'owner', 'authors') : null, 'members' => $study->allUsers(), 'preview' => $preview, 'availableRoles' => array_values(Jetstream::$roles), @@ -208,13 +208,15 @@ public function renderTabView($tab, $study, $team, $project, $license, $studyFSO public function moleculeStore(Request $request, Study $study) { + Gate::forUser($request->user())->authorize('updateStudy', $study); + $sample = $study->sample; if (! $sample) { $sample = Sample::create([ 'name' => $study->name.'_sample', 'slug' => Str::slug($study->name.'_sample', '-'), 'study_id' => $study->id, - 'project_id' => $study->project->id, + 'project_id' => $study->project ? $study->project->id : null, ]); $study->sample()->save($sample); } @@ -226,7 +228,9 @@ public function moleculeStore(Request $request, Study $study) ], [ 'molecular_formula' => $request->get('formula') ? $request->get('formula') : '', 'inchi_key' => $request->get('InChIKey') ? $request->get('InChIKey') : '', - 'sdf' => $request->get('mol') ? $request->get('mol') : '', + 'sdf' => $request->get('mol') + ? Sample::ensureMolfileHeader((string) $request->get('mol'), (string) $request->get('iupac_name', '')) + : '', 'canonical_smiles' => $request->get('canonical_smiles') ? $request->get('canonical_smiles') : '', ]); $sample->molecules()->syncWithPivotValues([$molecule->id], ['percentage_composition' => $request->get('percentage')], false); @@ -245,7 +249,20 @@ public function fetchNMRium(Request $request, Study $study) if (is_string($nmriumInfo)) { $nmriumInfo = json_decode($nmriumInfo, true); } - $nmriumInfo['data']['molecules'] = []; + if (! is_array($nmriumInfo)) { + $nmriumInfo = []; + } + if (! isset($nmriumInfo['data']) || ! is_array($nmriumInfo['data'])) { + $nmriumInfo['data'] = []; + } + if (! isset($nmriumInfo['data']['molecules']) || ! is_array($nmriumInfo['data']['molecules'])) { + $nmriumInfo['data']['molecules'] = []; + } + + if ($study->sample) { + $nmriumInfo['data']['molecules'] = $study->sample + ->mergeNmriumMolecules($nmriumInfo['data']['molecules']); + } return $nmriumInfo; } else { @@ -277,14 +294,13 @@ public function nmriumVersions(Request $request, Study $study) public function nmriumInfo(Request $request, Study $study) { - // $version = $request->get('version'); - // $spectra = $request->get('spectra'); - // $molecules = $nmriumInfo['data']['molecules']; - // $molecularInfo = $molecules; + Gate::forUser($request->user())->authorize('updateStudy', $study); + if ($study) { $user = Auth::user(); $data = $request->all(); $nmriumInfo = sanitizeUnicodeInNMRiumData($data); + $nmriumInfo = $this->normalizeNmriumMoleculeHeaders($nmriumInfo); $draft = $study->draft; $nmrium = $study->nmrium; if ($nmrium) { @@ -299,60 +315,43 @@ public function nmriumInfo(Request $request, Study $study) $study->has_nmrium = true; } $study->save(); - $_nmriumJSON = $nmriumInfo; foreach ($study->datasets as $dataset) { - $fsObject = $dataset->fsObject; - $studyFSObject = $study->fsObject; $datasetFSObject = $dataset->fsObject; - if ($draft && $draft->eln == 'chemotion') { - $path = '/'.$studyFSObject->name.'/'.$datasetFSObject->parent->name.'/'.$datasetFSObject->name; - } else { - $path = '/'.$studyFSObject->name.'/'.$datasetFSObject->name; + if (! $studyFSObject || ! $datasetFSObject) { + Log::warning('nmriumInfo: skipping dataset with missing fsObject', [ + 'study_id' => $study->id, + 'dataset_id' => $dataset->id, + 'study_fs_present' => (bool) $studyFSObject, + 'dataset_fs_present' => (bool) $datasetFSObject, + ]); + + continue; } - $fType = $studyFSObject->type; - $pathsMatch = false; - $spectrum = []; - $type = []; - foreach ($nmriumInfo['data']['spectra'] as $spectra) { - unset($_nmriumJSON['data']['spectra']); - $files = $spectra['sourceSelector']['files']; - if ($files) { - foreach ($files as $file) { - if (str_contains($file, $fType == 'file' ? $path : $path.'/')) { - $pathsMatch = true; - } - } - } - if ($pathsMatch) { - array_push($spectrum, $spectra); - $experimentDetailsExists = array_key_exists('experiment', $spectra['info']); - if ($experimentDetailsExists) { - $experiment = $spectra['info']['experiment']; - $nucleus = $spectra['info']['nucleus']; - if (is_array($nucleus)) { - $nucleus = implode('-', $nucleus); - } - array_push($type, $experiment.' - '.$nucleus); - } - $pathsMatch = false; - } + $isChemotion = $draft && $draft->eln === 'chemotion'; + $parentName = $isChemotion ? optional($datasetFSObject->parent)->name : null; + if ($isChemotion && $parentName === null) { + Log::warning('nmriumInfo: chemotion dataset without parent fsObject', [ + 'study_id' => $study->id, + 'dataset_id' => $dataset->id, + ]); + + continue; + } + + $mergedPayload = json_decode(json_encode($nmriumInfo), true); + if (! is_array($mergedPayload)) { + continue; } - if (count($spectrum) > 0) { - $_nmriumJSON['data']['spectra'] = $spectrum; - $_nmrium = $dataset->nmrium; - if ($_nmrium) { - $_nmrium->nmrium_info = $_nmriumJSON; - $dataset->has_nmrium = true; - $_nmrium->save(); - } else { - $_nmrium = NMRium::create([ - 'nmrium_info' => $_nmriumJSON, - ]); - $dataset->nmrium()->save($_nmrium); - $dataset->has_nmrium = true; + + $spectrum = BioschemasHelper::syncDatasetNmriumFromStudyPayload($dataset, $mergedPayload); + $type = []; + foreach ($spectrum as $spectra) { + $label = $this->spectrumTypeLabel($spectra); + if ($label !== null) { + array_push($type, $label); } } $uType = array_unique($type); @@ -363,6 +362,20 @@ public function nmriumInfo(Request $request, Study $study) } + // NMRium silently skips JCAMP-DX files that have no `XYDATA` + // block (e.g. MestReNova LINK files containing only a peak- + // assignment table or a chemical-structure block). Read those + // files' raw JCAMP headers so the upload sidebar still shows a + // sensible nucleus/data-type label instead of an empty card. + try { + (new JcampDatasetClassifier)->classifyStudy($study->fresh()); + } catch (\Throwable $e) { + Log::warning('JcampDatasetClassifier failed', [ + 'study_id' => $study->id, + 'error' => $e->getMessage(), + ]); + } + return $study->fresh(); } } @@ -380,12 +393,10 @@ public function moleculeDetach(Request $request, Study $study, Molecule $molecul public function files(Request $request, Study $study) { - if (! Gate::forUser($request->user())->check('viewStudy', $study)) { - throw new AuthorizationException; - } + Gate::forUser($request->user())->authorize('viewStudy', $study); $project = $study->project; - $team = $project->nonPersonalTeam; + $team = $project?->nonPersonalTeam; $studyFSObject = $study->fsObject; return $this->renderTabView('Files', $study, $team, $project, null, $studyFSObject, false); @@ -393,8 +404,10 @@ public function files(Request $request, Study $study) public function annotations(Request $request, Study $study) { - if (! Gate::forUser($request->user())->check('viewStudy', $study)) { - throw new AuthorizationException; + Gate::forUser($request->user())->authorize('viewStudy', $study); + + if (! $study->fsObject) { + return collect(); } $studyFSObject = FileSystemObject::with('children') @@ -405,6 +418,10 @@ public function annotations(Request $request, Study $study) ->orderBy('type') ->first(); + if (! $studyFSObject) { + return collect(); + } + return $studyFSObject->children->filter(function ($child) { return $child->instrument_type == 'nmredata' || $child->instrument_type == 'mol' || $child->instrument_type == 'sdf'; })->values(); @@ -439,7 +456,7 @@ public function file(Request $request, $code, Study $study, $filename) } else { if ($file) { - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $path = preg_replace( '~//+~', '/', @@ -497,6 +514,8 @@ public function Notifications(Request $request, Study $study) public function settings(Request $request, Study $study) { + Gate::forUser($request->user())->authorize('viewStudy', $study); + return Inertia::render('Study/Settings', [ 'study' => $study, 'project' => $study->project, @@ -508,6 +527,8 @@ public function destroy( StatefulGuard $guard, Study $study ) { + Gate::forUser($request->user())->authorize('deleteStudy', $study); + $confirmed = app(ConfirmPassword::class)( $guard, $request->user(), @@ -545,12 +566,174 @@ public function toggleStarred(Request $request, Study $study) public function snapshot(Request $request, Study $study) { + Gate::forUser($request->user())->authorize('updateStudy', $study); + $content = $request->get('img'); if ($content) { $path = '/projects/'.$study->project->uuid.'/'.$study->slug.'.svg'; - Storage::disk(env('FILESYSTEM_DRIVER_PUBLIC'))->put($path, $content, 'public'); + Storage::disk(config('filesystems.default_public'))->put($path, $content, 'public'); $study->study_photo_path = $path; $study->save(); } } + + /** + * Ensure each entry in `data.molecules[*].molfile` keeps a valid 3-line + * MOL header (title, generator, comment) before the V2000/V3000 counts + * line. NMRium and the chemistry standardize endpoint occasionally emit + * molfiles without the title line, which silently breaks parsers on + * subsequent reload. We never strip or escape the line; we only prepend + * blank lines (or the molecule's label as the title) when the header has + * been collapsed below 3 lines. See `Sample::ensureMolfileHeader` for + * details. + * + * @param array $nmriumInfo + * @return array + */ + protected function normalizeNmriumMoleculeHeaders(array $nmriumInfo): array + { + $molecules = $nmriumInfo['data']['molecules'] ?? null; + if (! is_array($molecules)) { + return $nmriumInfo; + } + + foreach ($molecules as $idx => $entry) { + if (! is_array($entry)) { + continue; + } + $molfile = $entry['molfile'] ?? null; + if (! is_string($molfile) || $molfile === '') { + continue; + } + $label = isset($entry['label']) ? (string) $entry['label'] : ''; + $molecules[$idx]['molfile'] = Sample::ensureMolfileHeader($molfile, $label); + } + + $nmriumInfo['data']['molecules'] = $molecules; + + return $nmriumInfo; + } + + /** + * Build a human-readable spectrum type label such as `1H NMR - 1D` from + * an NMRium spectrum payload, falling back to a path-based dimensionality + * guess when the parser-derived `info` block is missing or has been + * corrupted by a legacy save (older builds dropped `experiment`/`nucleus`). + * + * @param array $spectrum + */ + protected function spectrumTypeLabel(array $spectrum): ?string + { + $info = is_array($spectrum['info'] ?? null) ? $spectrum['info'] : []; + + $experiment = isset($info['experiment']) && is_string($info['experiment']) && $info['experiment'] !== '' + ? $this->formatExperimentName($info['experiment']) + : null; + + $nucleus = $info['nucleus'] ?? null; + if (is_array($nucleus)) { + $nucleus = implode('-', array_filter(array_map('strval', $nucleus), fn ($v) => $v !== '')); + } elseif (! is_string($nucleus)) { + $nucleus = null; + } + if ($nucleus === '') { + $nucleus = null; + } + + $dimension = null; + if (isset($info['dimension']) && is_numeric($info['dimension'])) { + $dimension = (int) $info['dimension']; + } else { + $dimension = $this->guessSpectrumDimension($spectrum); + } + + if ($experiment === null && $dimension === null && $nucleus === null) { + return null; + } + + if ($experiment === null && $dimension !== null) { + $experiment = $dimension.'D'; + } + + if ($nucleus !== null && $experiment !== null) { + return $nucleus.' NMR - '.$experiment; + } + + if ($nucleus !== null) { + return $nucleus.' NMR'; + } + + return ($experiment ?? '').' NMR'; + } + + /** + * Render NMRium's lowercase experiment tokens (`hsqc`, `cosy`, `1d`, …) + * in their conventional uppercase form for display. Unknown values are + * passed through unchanged. + */ + protected function formatExperimentName(string $experiment): string + { + $trimmed = trim($experiment); + if ($trimmed === '') { + return $experiment; + } + + $lower = strtolower($trimmed); + + if (preg_match('/^(\d+)d$/', $lower, $m)) { + return $m[1].'D'; + } + + $known = [ + 'cosy', 'noesy', 'roesy', 'tocsy', 'hsqc', 'hmbc', 'hmqc', + 'dept', 'dept45', 'dept90', 'dept135', 'jres', 'inadequate', + 'apt', 'edited-hsqc', 'hsqc-tocsy', + ]; + if (in_array($lower, $known, true)) { + return strtoupper($lower); + } + + return $trimmed; + } + + /** + * Heuristically infer 1D vs 2D from an NMRium spectrum's source selector + * file paths (Bruker conventions: `acqu2s` / `pdata/.../2[ri]+` => 2D, + * `acqus` / `pdata/.../1[ri]` / `fid` => 1D). + * + * @param array $spectrum + */ + protected function guessSpectrumDimension(array $spectrum): ?int + { + $selector = $spectrum['sourceSelector'] ?? $spectrum['selector'] ?? []; + $files = is_array($selector['files'] ?? null) ? $selector['files'] : []; + if (empty($files)) { + return null; + } + + foreach ($files as $file) { + if (! is_string($file)) { + continue; + } + $base = strtolower(basename($file)); + if (in_array($base, ['acqu2s', 'acqu3s', '2rr', '2ri', '2ir', '2ii', '3rrr'], true)) { + return $base === '3rrr' ? 3 : 2; + } + if (preg_match('#/pdata/\d+/2[ri]+$#i', $file)) { + return 2; + } + } + + foreach ($files as $file) { + if (! is_string($file)) { + continue; + } + $base = strtolower(basename($file)); + if (in_array($base, ['acqus', '1r', '1i', 'fid'], true)) { + return 1; + } + } + + return null; + } } diff --git a/app/Http/Controllers/StudyInvitationController.php b/app/Http/Controllers/StudyInvitationController.php index 809895185..7461355cc 100644 --- a/app/Http/Controllers/StudyInvitationController.php +++ b/app/Http/Controllers/StudyInvitationController.php @@ -5,6 +5,7 @@ use App\Actions\Study\AddStudyMember; use App\Models\StudyInvitation; use Illuminate\Auth\Access\AuthorizationException; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Gate; @@ -14,7 +15,7 @@ class StudyInvitationController extends Controller * Accept a study invitation. * * @param \Laravel\Jetstream\StudyInvitation $invitation - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function acceptInvitation(Request $request, StudyInvitation $invitation) { @@ -36,7 +37,7 @@ public function acceptInvitation(Request $request, StudyInvitation $invitation) * Cancel the given study invitation. * * @param \Laravel\Jetstream\StudyInvitation $invitation - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function destroyInvitation(Request $request, StudyInvitation $invitation) { diff --git a/app/Http/Controllers/StudyMemberController.php b/app/Http/Controllers/StudyMemberController.php index fe68dfa8a..3e9b61753 100644 --- a/app/Http/Controllers/StudyMemberController.php +++ b/app/Http/Controllers/StudyMemberController.php @@ -7,6 +7,7 @@ use App\Actions\Study\UpdateStudyMemberRole; use App\Models\Study; use App\Models\User; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class StudyMemberController extends Controller @@ -15,7 +16,7 @@ class StudyMemberController extends Controller * Add a new team member to a study. * * @param int $studyId - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function memberStore(Request $request, $studyId) { @@ -37,7 +38,7 @@ public function memberStore(Request $request, $studyId) * * @param int $studyId * @param int $userId - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function updateMemberRole(Request $request, $studyId, $userId) { @@ -56,7 +57,7 @@ public function updateMemberRole(Request $request, $studyId, $userId) * * @param int $studyId * @param int $userId - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function removeMember(Request $request, $studyId, $userId) { diff --git a/app/Http/Controllers/SupportBubbleController.php b/app/Http/Controllers/SupportBubbleController.php new file mode 100644 index 000000000..96c417ef3 --- /dev/null +++ b/app/Http/Controllers/SupportBubbleController.php @@ -0,0 +1,46 @@ +input('subject'), + $request->input('message'), + $request->input('email'), + $request->input('name'), + $request->input('url'), + $request->ip(), + $request->userAgent(), + $request + )); + + return response()->view('support-bubble::success'); + + } catch (\Exception $e) { + Log::error('Support bubble submission failed', [ + 'error' => $e->getMessage(), + 'ip' => $request->ip(), + 'data' => $request->only(['email', 'subject']), + ]); + + return response()->json([ + 'success' => false, + 'message' => 'An error occurred while processing your request. Please try again.', + ], 500); + } + } +} diff --git a/app/Http/Controllers/TeamController.php b/app/Http/Controllers/TeamController.php index 5effac6eb..4bc5ea5e1 100644 --- a/app/Http/Controllers/TeamController.php +++ b/app/Http/Controllers/TeamController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use Illuminate\Contracts\Auth\StatefulGuard; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; use Laravel\Fortify\Actions\ConfirmPassword; @@ -19,7 +20,7 @@ class TeamController extends Controller * Delete the given team. * * @param int $teamId - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function destroy(Request $request, StatefulGuard $guard, $teamId) { diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index 9b38a06a8..25f4e8e18 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -14,8 +14,22 @@ class UploadController extends Controller { public function upload(Request $request) { + $draftId = $request->get('draft_id'); + + if (is_numeric($draftId)) { + $draft = Draft::find((int) $draftId); + + if ($draft) { + $project = Project::where('draft_id', $draft->id)->first(); + + if ($project && $project->status !== 'draft') { + return redirect()->route('publish', ['draft' => $draft->id]); + } + } + } + return Inertia::render('Upload', [ - 'draft_id' => $request->get('draft_id'), + 'draft_id' => $draftId, 'step' => $request->get('step'), ]); } diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index e4d527f77..d0840c4c6 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -42,46 +42,59 @@ public function share(Request $request) $user = $request->user(); return array_merge(parent::share($request), [ - 'flash' => [ - 'message' => fn () => $request->session()->get('message'), - ], - 'auth.user.permissions' => fn () => $user ? - $user->getPermissionsViaRoles()->pluck('name') - : null, 'flash' => function () use ($request) { return [ + 'message' => $request->session()->get('message'), 'success' => $request->session()->get('success'), 'error' => $request->session()->get('error'), + 'publish_validation_hints' => $request->session()->get('publish_validation_hints'), ]; }, + 'auth.user.permissions' => fn () => $user ? + $user->getPermissionsViaRoles()->pluck('name') + : null, 'auth.user.roles' => fn () => $user ? $user->getRoleNames() : null, 'auth.user.teamRole' => fn () => $user ? $user->teamRole($user->currentTeam) : null, 'auth.user.notifications' => fn () => $user ? $user->unreadNotifications : null, - 'twitter' => (env('TWITTER_CLIENT_ID') !== null && env('TWITTER_CLIENT_ID') !== ''), - 'github' => (env('GITHUB_CLIENT_ID') !== null && env('GITHUB_CLIENT_ID') !== ''), - 'orcid' => (env('ORCID_CLIENT_ID') !== null && env('ORCID_CLIENT_ID') !== ''), - 'nfdiaai' => (env('NFDIAAI_CLIENT_ID') !== null && env('NFDIAAI_CLIENT_ID') !== ''), + 'twitter' => (config('services.twitter.client_id') !== null && config('services.twitter.client_id') !== ''), + 'github' => (config('services.github.client_id') !== null && config('services.github.client_id') !== ''), + 'orcid' => (config('services.orcid.client_id') !== null && config('services.orcid.client_id') !== ''), + 'nfdiaai' => (config('services.regapp.client_id') !== null && config('services.regapp.client_id') !== ''), 'config.announcements' => Schema::hasTable('announcements') ? Announcement::active() : null, - 'url' => env('APP_URL'), - 'nmriumURL' => env('NMRIUM_URL'), + 'url' => config('app.url'), + 'nmriumURL' => config('external-links.nmrium_url'), + 'spectraParserUrl' => rtrim((string) config('external-links.nmrkit_url'), '/').'/latest/spectra/parse/url', 'team' => $user ? $user->currentTeam : null, - 'environment' => env('APP_ENV'), - 'MEILISEARCH_HOST' => (env('MEILISEARCH_HOST')), - 'MEILISEARCH_PUBLICKEY' => (env('MEILISEARCH_PUBLICKEY')), - 'SCOUT_PREFIX' => (env('SCOUT_PREFIX')), - 'europemcWSApi' => (env('EUROPEMC_WS_API')), - 'dataciteURL' => env('DATACITE_ENDPOINT'), - 'coolOffPeriod' => env('COOL_OFF_PERIOD'), - 'mailFromAddress' => env('MAIL_FROM_ADDRESS'), - 'orcidSearchApi' => env('ORCID_ID_SEARCH_API'), - 'orcidPersonApi' => env('ORCID_ID_PERSON_API'), - 'orcidEmploymentApi' => env('ORCID_ID_EMPLOYMENT_API'), - 'CM_API' => env('CM_API'), - 'CROSSREF_API' => env('CROSSREF_API'), - 'DATACITE_API' => env('DATACITE_API'), + 'environment' => config('app.env'), + 'MEILISEARCH_HOST' => config('scout.meilisearch.host'), + 'MEILISEARCH_PUBLICKEY' => config('scout.meilisearch.public_key'), + 'SCOUT_PREFIX' => config('scout.prefix'), + 'europemcWSApi' => config('external-links.europemc_ws_api'), + 'dataciteURL' => config('doi.datacite.endpoint'), + 'coolOffPeriod' => config('nmrxiv.cool_off_period'), + 'mailFromAddress' => config('mail.from.address'), + 'dashboardWorkspace' => function () use ($request) { + if (! $request->routeIs('dashboard')) { + return null; + } + + $workspace = $request->query('workspace'); + + return in_array($workspace, ['shared', 'recent', 'starred', 'trashed'], true) + ? $workspace + : 'default'; + }, + 'chemistryStandardizeUrl' => route('chemistry.standardize'), + 'orcidSearchApi' => config('orcid.search_api'), + 'orcidPersonApi' => config('orcid.person_api'), + 'michiStandardsUrl' => config('external-links.michi_standards_url'), + 'orcidEmploymentApi' => config('orcid.employment_api'), + 'CM_API' => config('external-links.cm_api'), + 'CROSSREF_API' => config('external-links.crossref_api'), + 'DATACITE_API' => config('external-links.datacite_api'), ]); } } diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index a9d8552f4..aa0fa6057 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -10,9 +10,29 @@ class TrustProxies extends Middleware /** * The trusted proxies for this application. * + * Configured via the TRUSTED_PROXIES env var. Defaults to "*" only in + * production where a real reverse proxy terminates TLS. In local/Docker + * dev we trust nothing so Laravel\Sentinel does not flag every request + * coming in via the host interface as a public-proxy access attempt. + * * @var array|string|null */ - protected $proxies = '*'; + protected $proxies; + + public function __construct() + { + $configured = config('app.trusted_proxies', env('TRUSTED_PROXIES')); + + if ($configured === null) { + $this->proxies = app()->environment('production') ? '*' : null; + + return; + } + + $this->proxies = $configured === '*' + ? '*' + : array_filter(array_map('trim', explode(',', (string) $configured))); + } /** * The headers that should be used to detect proxies. diff --git a/app/Http/Middleware/XFrameOptions.php b/app/Http/Middleware/XFrameOptions.php index 9b4e237f6..b22d47e1b 100644 --- a/app/Http/Middleware/XFrameOptions.php +++ b/app/Http/Middleware/XFrameOptions.php @@ -11,7 +11,7 @@ class XFrameOptions /** * Handle an incoming request. * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + * @param Closure(Request): (Response) $next */ public function handle(Request $request, Closure $next): Response { diff --git a/app/Http/Requests/DashboardIndexRequest.php b/app/Http/Requests/DashboardIndexRequest.php new file mode 100644 index 000000000..1d9118590 --- /dev/null +++ b/app/Http/Requests/DashboardIndexRequest.php @@ -0,0 +1,104 @@ +user() !== null; + } + + /** + * @return array|string> + */ + public function rules(): array + { + return [ + 'tab' => ['nullable', 'string', Rule::in(['projects', 'samples'])], + 'projects_page' => ['nullable', 'integer', 'min:1'], + 'samples_page' => ['nullable', 'integer', 'min:1'], + 'projects_per_page' => ['nullable', 'integer', 'min:1', 'max:50'], + 'samples_per_page' => ['nullable', 'integer', 'min:1', 'max:50'], + 'projects_q' => ['nullable', 'string', 'max:255'], + 'samples_q' => ['nullable', 'string', 'max:255'], + 'projects_status' => ['nullable', 'string', Rule::in(['all', 'draft', 'published', 'archived', 'embargo'])], + 'samples_status' => ['nullable', 'string', Rule::in(['all', 'public', 'private'])], + 'workspace' => ['nullable', 'string', Rule::in(['default', 'shared', 'recent', 'starred', 'trashed'])], + 'action' => ['nullable', 'string', 'max:255'], + 'draft_id' => ['nullable', 'integer'], + ]; + } + + protected function prepareForValidation(): void + { + $tab = $this->query('tab'); + if (! in_array($tab, ['projects', 'samples'], true)) { + $tab = 'projects'; + } + + $projectsStatus = $this->query('projects_status'); + if (! in_array($projectsStatus, ['all', 'draft', 'published', 'archived', 'embargo'], true)) { + $projectsStatus = 'all'; + } + + $samplesStatus = $this->query('samples_status'); + if (! in_array($samplesStatus, ['all', 'public', 'private'], true)) { + $samplesStatus = 'all'; + } + + $workspace = $this->query('workspace'); + if (! in_array($workspace, ['shared', 'recent', 'starred', 'trashed'], true)) { + $workspace = 'default'; + } + + $this->merge([ + 'tab' => $tab, + 'workspace' => $workspace, + 'projects_status' => $projectsStatus, + 'samples_status' => $samplesStatus, + 'projects_page' => max(1, (int) $this->query('projects_page', 1)), + 'samples_page' => max(1, (int) $this->query('samples_page', 1)), + 'projects_per_page' => max(1, min(50, (int) $this->query('projects_per_page', 10))), + 'samples_per_page' => max(1, min(50, (int) $this->query('samples_per_page', 12))), + ]); + } + + /** + * Normalized dashboard query state for Inertia and redirects. + * + * @return array + */ + public function dashboardFilters(): array + { + $projectsPerPage = min(max(1, (int) $this->input('projects_per_page', 10)), 50); + $samplesPerPage = min(max(1, (int) $this->input('samples_per_page', 12)), 50); + + $projectsQ = trim((string) $this->input('projects_q', '')); + $samplesQ = trim((string) $this->input('samples_q', '')); + + return [ + 'tab' => $this->input('tab', 'projects'), + 'workspace' => $this->input('workspace', 'default'), + 'projects_page' => max(1, (int) $this->input('projects_page', 1)), + 'samples_page' => max(1, (int) $this->input('samples_page', 1)), + 'projects_per_page' => $projectsPerPage, + 'samples_per_page' => $samplesPerPage, + 'projects_q' => $projectsQ, + 'samples_q' => $samplesQ, + 'projects_status' => $this->input('projects_status', 'all'), + 'samples_status' => $this->input('samples_status', 'all'), + 'action' => $this->query('action'), + 'draft_id' => $this->query('draft_id') !== null && $this->query('draft_id') !== '' + ? (int) $this->query('draft_id') + : null, + ]; + } +} diff --git a/app/Http/Requests/SupportBubbleRequest.php b/app/Http/Requests/SupportBubbleRequest.php new file mode 100644 index 000000000..f44e40c99 --- /dev/null +++ b/app/Http/Requests/SupportBubbleRequest.php @@ -0,0 +1,267 @@ +|string> + */ + public function rules(): array + { + return [ + 'email' => [ + 'required', + 'email', + 'max:255', + function ($attribute, $value, $fail) { + // Block disposable email domains + $disposableDomains = [ + '10minutemail.com', '10minutemail.net', 'guerrillamail.com', + 'mailinator.com', 'yopmail.com', 'tempmail.org', 'throwaway.email', + 'temp-mail.org', 'getnada.com', 'maildrop.cc', + ]; + + $domain = substr(strrchr($value, '@'), 1); + if (in_array(strtolower($domain), $disposableDomains)) { + $fail('Please use a permanent email address. Temporary email services are not allowed.'); + } + + // Extract username part (before @) + $username = substr($value, 0, strpos($value, '@')); + + // Enhanced gibberish detection for email usernames + if ($this->isGibberishEmail($username)) { + $fail('Please enter a valid email address. Random characters are not allowed.'); + } + + // Block suspicious patterns (original check) + if (preg_match('/^[a-z0-9]{10,20}@gmail\.com$/i', $value)) { + $fail('Please enter a valid email address. Random characters are not allowed.'); + } + }, + ], + 'name' => [ + 'nullable', + 'string', + 'max:255', + function ($attribute, $value, $fail) { + if ($value && $this->containsGibberish($value)) { + $fail('The name contains invalid characters or patterns.'); + } + }, + ], + 'subject' => [ + 'required', + 'string', + 'max:255', + 'min:3', + function ($attribute, $value, $fail) { + if ($this->containsGibberish($value)) { + $fail('The subject must contain meaningful text.'); + } + + // Check for excessive special characters + if (preg_match_all('/[^a-zA-Z0-9\s]/', $value) > strlen($value) * 0.3) { + $fail('The subject contains too many special characters.'); + } + }, + ], + 'message' => [ + 'required', + 'string', + 'max:2000', + 'min:10', + function ($attribute, $value, $fail) { + if ($this->containsGibberish($value)) { + $fail('The message must contain meaningful text.'); + } + + // Check for excessive special characters + if (preg_match_all('/[^a-zA-Z0-9\s\.\,\!\?\-]/', $value) > strlen($value) * 0.2) { + $fail('The message contains too many special characters.'); + } + + // Check for minimum word count + $wordCount = str_word_count($value); + if ($wordCount < 3) { + $fail('The message must contain at least 3 words.'); + } + }, + ], + 'url' => 'nullable|url|max:255', + 'g-recaptcha-response' => [ + function ($attribute, $value, $fail) { + if (config('services.recaptcha.site_key') && empty($value)) { + $fail('Please complete the CAPTCHA verification.'); + + return; + } + if (config('services.recaptcha.site_key') && ! empty($value)) { + $rule = new RecaptchaRule; + $rule->validate($attribute, $value, $fail); + } + }, + ], + ]; + } + + /** + * Get custom messages for validation errors. + */ + public function messages(): array + { + return [ + 'subject.min' => 'The subject must be at least 3 characters long.', + 'message.min' => 'The message must be at least 10 characters long.', + 'email.required' => 'An email address is required.', + 'email.email' => 'Please enter a valid email address format (e.g., name@domain.com).', + 'subject.required' => 'A subject is required.', + 'message.required' => 'A message is required.', + ]; + } + + /** + * Check if text contains gibberish or random character patterns + */ + protected function containsGibberish(string $text): bool + { + // Allow common words and normal text + if (strlen($text) < 15) { + return false; // Don't check short text like "Hello" + } + + // Check for random character patterns (like "αχΥηΤrvnIbuQzoGkkbYqjEr") + // High ratio of consonants without vowels + $consonantRatio = preg_match_all('/[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]/', $text); + $vowelRatio = preg_match_all('/[aeiouAEIOU]/', $text); + + if (strlen($text) > 20 && $vowelRatio == 0) { + return true; + } + + if (strlen($text) > 15 && $consonantRatio / max(strlen($text), 1) > 0.85) { + return true; + } + + // Check for mixed scripts (Latin + Greek/Cyrillic like in spam) + $hasLatin = preg_match('/[a-zA-Z]/', $text); + $hasGreek = preg_match('/[\x{0370}-\x{03FF}]/u', $text); + $hasCyrillic = preg_match('/[\x{0400}-\x{04FF}]/u', $text); + + if (($hasLatin && $hasGreek) || ($hasLatin && $hasCyrillic)) { + return true; + } + + // Check for alternating case patterns (like "αχΥηΤrvnIbu") + if (preg_match('/([a-z][A-Z]){4,}|([A-Z][a-z]){4,}/', $text)) { + return true; + } + + // Check for sequences of random characters + if (preg_match('/[a-zA-Z]{20,}/', $text) && ! preg_match('/\s/', $text)) { + return true; + } + + return false; + } + + /** + * Check if email username contains gibberish patterns + */ + protected function isGibberishEmail(string $username): bool + { + // Allow common email patterns first + if (strlen($username) < 6) { + return false; // Allow short usernames like "john", "mary" + } + + // Check for too many consecutive consonants (like "jkdkfjdks") + if (preg_match('/[bcdfghjklmnpqrstvwxyz]{4,}/i', $username)) { + return true; + } + + // Check for alternating consonant patterns without vowels + $consonantCount = preg_match_all('/[bcdfghjklmnpqrstvwxyz]/i', $username); + $vowelCount = preg_match_all('/[aeiou]/i', $username); + + // If username is mostly consonants (>80%) and longer than 6 chars, likely gibberish + if (strlen($username) > 6 && $consonantCount > 0 && ($vowelCount / max($consonantCount, 1)) < 0.25) { + return true; + } + + // Check for patterns like repeated character groups "jkjk", "dkdk" + if (preg_match('/(.{2,3})\1{2,}/', $username)) { + return true; + } + + // Check for keyboard patterns like "qwerty", "asdf", etc. + $keyboardPatterns = [ + 'qwert', 'asdf', 'zxcv', 'uiop', 'hjkl', 'bnm', + 'poiu', 'lkjh', 'mnbv', 'rewq', 'fdsa', 'vcxz', + ]; + + foreach ($keyboardPatterns as $pattern) { + if (stripos($username, $pattern) !== false) { + return true; + } + } + + // Check for completely random character sequences + // If no vowels and more than 8 characters, likely random + if (strlen($username) > 8 && $vowelCount == 0) { + return true; + } + + return false; + } + + /** + * Handle a failed validation attempt. + */ + protected function failedValidation(Validator $validator): void + { + // Log suspicious attempts for monitoring + if ($this->containsSpamIndicators()) { + Log::warning('Potential spam attempt blocked on support bubble', [ + 'ip' => $this->ip(), + 'user_agent' => $this->userAgent(), + 'data' => $this->only(['email', 'subject', 'message']), + 'errors' => $validator->errors()->toArray(), + ]); + } + + parent::failedValidation($validator); + } + + /** + * Check if the request contains spam indicators + */ + protected function containsSpamIndicators(): bool + { + $email = $this->input('email', ''); + $subject = $this->input('subject', ''); + $message = $this->input('message', ''); + + // Check for the specific patterns seen in the spam + return $this->containsGibberish($subject) || + $this->containsGibberish($message) || + preg_match('/^[a-z0-9]{10,20}@gmail\.com$/i', $email); + } +} diff --git a/app/Http/Resources/AuthorResource.php b/app/Http/Resources/AuthorResource.php index 646a82a78..ed96439cf 100644 --- a/app/Http/Resources/AuthorResource.php +++ b/app/Http/Resources/AuthorResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class AuthorResource extends JsonResource @@ -9,7 +10,7 @@ class AuthorResource extends JsonResource /** * Transform the author resource into an array. * - * @param \Illuminate\Http\Request $request + * @param Request $request * @return array */ public function toArray($request): array @@ -25,9 +26,13 @@ public function toArray($request): array 'affiliation' => $this->affiliation, 'contributor_type' => $this->whenPivotLoaded('author_project', function () { return $this->pivot->contributor_type; + }) ?? $this->whenPivotLoaded('author_study', function () { + return $this->pivot->contributor_type; }), 'sort_order' => $this->whenPivotLoaded('author_project', function () { return $this->pivot->sort_order; + }) ?? $this->whenPivotLoaded('author_study', function () { + return $this->pivot->sort_order; }), 'created_at' => $this->created_at?->toISOString(), 'updated_at' => $this->updated_at?->toISOString(), diff --git a/app/Http/Resources/CitationResource.php b/app/Http/Resources/CitationResource.php index b52080b65..decb39e26 100644 --- a/app/Http/Resources/CitationResource.php +++ b/app/Http/Resources/CitationResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class CitationResource extends JsonResource @@ -9,7 +10,7 @@ class CitationResource extends JsonResource /** * Transform the citation resource into an array. * - * @param \Illuminate\Http\Request $request + * @param Request $request * @return array */ public function toArray($request): array @@ -20,9 +21,7 @@ public function toArray($request): array 'title' => $this->title, 'authors' => $this->authors, 'citation_text' => $this->citation_text, - 'user_id' => $this->whenPivotLoaded('citation_project', function () { - return $this->pivot->user ?? null; - }), + 'user_id' => data_get($this->resource, 'pivot.user'), 'created_at' => $this->created_at?->toISOString(), 'updated_at' => $this->updated_at?->toISOString(), ]; diff --git a/app/Http/Resources/DatasetResource.php b/app/Http/Resources/DatasetResource.php index f43983eb8..17f046246 100644 --- a/app/Http/Resources/DatasetResource.php +++ b/app/Http/Resources/DatasetResource.php @@ -2,6 +2,8 @@ namespace App\Http\Resources; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class DatasetResource extends JsonResource @@ -23,8 +25,8 @@ public function lite(bool $lite, ?array $properties = []): self /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { @@ -52,6 +54,11 @@ public function toArray($request): array 'external_url' => $this->external_url, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, + $this->mergeWhen(in_array('nmrium', $this->properties), function () { + return [ + 'nmrium_info' => $this->resource->normalizedNmriumInfo(), + ]; + }), ]; } } diff --git a/app/Http/Resources/FileSystemObjectResource.php b/app/Http/Resources/FileSystemObjectResource.php index fce0a8e7d..c2e76698e 100644 --- a/app/Http/Resources/FileSystemObjectResource.php +++ b/app/Http/Resources/FileSystemObjectResource.php @@ -2,6 +2,8 @@ namespace App\Http\Resources; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class FileSystemObjectResource extends JsonResource @@ -9,8 +11,8 @@ class FileSystemObjectResource extends JsonResource /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { diff --git a/app/Http/Resources/LicenseResource.php b/app/Http/Resources/LicenseResource.php index 05d4e667b..c75e6ed91 100644 --- a/app/Http/Resources/LicenseResource.php +++ b/app/Http/Resources/LicenseResource.php @@ -2,6 +2,8 @@ namespace App\Http\Resources; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class LicenseResource extends JsonResource @@ -9,8 +11,8 @@ class LicenseResource extends JsonResource /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { diff --git a/app/Http/Resources/ProjectResource.php b/app/Http/Resources/ProjectResource.php index 793430c34..fbbebd692 100644 --- a/app/Http/Resources/ProjectResource.php +++ b/app/Http/Resources/ProjectResource.php @@ -3,7 +3,11 @@ namespace App\Http\Resources; use App\Models\FileSystemObject; +use App\Models\Project; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Facades\Gate; class ProjectResource extends JsonResource { @@ -24,8 +28,8 @@ public function lite(bool $lite, ?array $properties = []): self /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { @@ -52,6 +56,7 @@ public function toArray($request): array 'stats' => [ 'likes' => $this->likesCount(), ], + 'samples_count' => $this->samplesCountForTab($request), 'license' => new LicenseResource( $this->license ), @@ -118,4 +123,22 @@ function () { }), ]; } + + /** + * Count for the Samples tab: public studies for public projects; all studies for members on private projects. + */ + protected function samplesCountForTab(Request $request): int + { + /** @var Project $project */ + $project = $this->resource; + + if (! $project->is_public) { + $user = $request->user(); + if ($user !== null && Gate::forUser($user)->check('viewProject', $project)) { + return (int) $project->studies()->count(); + } + } + + return (int) $project->studies()->where('is_public', true)->count(); + } } diff --git a/app/Http/Resources/SampleResource.php b/app/Http/Resources/SampleResource.php index 46922821d..acd4ede8e 100644 --- a/app/Http/Resources/SampleResource.php +++ b/app/Http/Resources/SampleResource.php @@ -2,6 +2,8 @@ namespace App\Http\Resources; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class SampleResource extends JsonResource @@ -9,8 +11,8 @@ class SampleResource extends JsonResource /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { diff --git a/app/Http/Resources/StudyResource.php b/app/Http/Resources/StudyResource.php index 4aa2c4b40..2514918b2 100644 --- a/app/Http/Resources/StudyResource.php +++ b/app/Http/Resources/StudyResource.php @@ -2,13 +2,15 @@ namespace App\Http\Resources; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class StudyResource extends JsonResource { private bool $lite = true; - private array $properties = ['sample', 'users', 'license']; + private array $properties = ['sample', 'users', 'license', 'authors']; public function lite(bool $lite, ?array $properties = []): self { @@ -23,8 +25,8 @@ public function lite(bool $lite, ?array $properties = []): self /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { @@ -33,7 +35,7 @@ public function toArray($request): array 'name' => $this->name, 'slug' => $this->slug, 'description' => $this->description, - 'molecules' => $this->sample->molecules, + 'molecules' => $this->sample ? $this->sample->molecules : [], 'team' => $this->when(! ($this->team && $this->team->personal_team), $this->team), 'photo_url' => $this->study_photo_url, 'tags' => $this->tags, @@ -112,6 +114,32 @@ function () { ), ]; }), + $this->mergeWhen(! $this->lite, function () { + return [ + $this->mergeWhen( + in_array('authors', $this->properties), + function () { + return [ + 'authors' => $this->studyAuthors ? AuthorResource::collection($this->studyAuthors) : [], + ]; + } + ), + ]; + }), + $this->mergeWhen(! $this->lite, function () { + return [ + $this->mergeWhen( + in_array('citations', $this->properties), + function () { + return [ + 'citations' => CitationResource::collection( + $this->relationLoaded('linkedCitations') ? $this->linkedCitations : collect() + ), + ]; + } + ), + ]; + }), ]; } } diff --git a/app/Http/Resources/TeamResource.php b/app/Http/Resources/TeamResource.php index fd1c38c6f..378aba3bc 100644 --- a/app/Http/Resources/TeamResource.php +++ b/app/Http/Resources/TeamResource.php @@ -2,6 +2,8 @@ namespace App\Http\Resources; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class TeamResource extends JsonResource @@ -9,8 +11,8 @@ class TeamResource extends JsonResource /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index f1d59d26c..27ce26607 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -2,6 +2,8 @@ namespace App\Http\Resources; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource @@ -9,8 +11,8 @@ class UserResource extends JsonResource /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable + * @param Request $request + * @return array|Arrayable|\JsonSerializable */ public function toArray($request): array { diff --git a/app/Jobs/ArchiveProject.php b/app/Jobs/ArchiveProject.php index a5a5bf54b..07fbf73a0 100644 --- a/app/Jobs/ArchiveProject.php +++ b/app/Jobs/ArchiveProject.php @@ -21,10 +21,17 @@ class ArchiveProject implements ShouldBeUnique, ShouldQueue public $timeout = 0; + /** + * Only ever run an archive job once. Failures must surface to Horizon + * rather than be retried, otherwise large zips re-run from scratch and + * compound the load on Ceph. + */ + public $tries = 1; + /** * The project instance. * - * @var \App\Models\Project + * @var Project */ public $project; @@ -44,11 +51,26 @@ public function uniqueId(): string return $this->project->id; } + /** + * How long (seconds) the unique lock should be held. Without this, + * a crashed/killed worker can leave the lock orphaned in redis and + * silently swallow every subsequent dispatch for this project. + */ + public function uniqueFor(): int + { + return 14400; + } + /** * Execute the job. */ public function handle(): void { + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_project_job_started', + 'project_id' => $this->project->id, + ]); + $project = $this->project; if ($project) { $archiveDownloadURL = $project->download_url; @@ -63,7 +85,7 @@ public function handle(): void $fsObject = new FileSystemObject; $fsObject->type = 'directory'; $fsObject->name = $project->slug; - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $fsObject->path = $environment.'/'.$project->uuid; $fsObject->key = $project->uuid; $fsObject->status = 'present'; @@ -73,11 +95,11 @@ public function handle(): void if ($fsObject && $fsObject->status != 'missing') { $path = $fsObject->path; $s3Client = $this->storageClient(); - $bucket = config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.bucket'); + $bucket = config('filesystems.disks.'.config('filesystems.default').'.bucket'); $s3keys = []; - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); if ($fsObject->type == 'file') { - if (Storage::disk(env('FILESYSTEM_DRIVER'))->exists($path)) { + if (Storage::disk(config('filesystems.default'))->exists($path)) { array_push($s3keys, substr($fsObject->path, 1)); } } else { @@ -163,37 +185,60 @@ public function handle(): void $zip->finish(); fclose($archiveDestination); - Storage::disk(env('FILESYSTEM_DRIVER'))->setVisibility($zipFilePath, 'public'); - $url = Storage::disk(env('FILESYSTEM_DRIVER'))->url($zipFilePath); + Storage::disk(config('filesystems.default'))->setVisibility($zipFilePath, 'public'); + $url = Storage::disk(config('filesystems.default'))->url($zipFilePath); $project->download_url = $url; $project->internal_status = 'complete'; $project->save(); + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_project_zip_built', + 'project_id' => $project->id, + 'download_url_set' => true, + ]); + } else { + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_project_skip_zip', + 'project_id' => $project->id, + 'reason' => ! $fsObject ? 'no_fs_object' : 'fs_status_missing', + ]); } } else { + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_project_skip_existing_download_url', + 'project_id' => $project->id, + ]); + $project->internal_status = 'complete'; $project->save(); } } } + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_project_job_finished', + 'project_id' => $this->project->id, + ]); } /** * Get the S3 storage client instance. * - * @return \Aws\S3\S3Client + * @return S3Client */ protected function storageClient() { + $diskName = config('filesystems.default'); $config = [ - 'region' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.region'), + 'region' => config('filesystems.disks.'.$diskName.'.region'), 'version' => 'latest', 'use_path_style_endpoint' => true, - 'url' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.endpoint'), - 'endpoint' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.endpoint'), + 'url' => config('filesystems.disks.'.$diskName.'.endpoint'), + 'endpoint' => config('filesystems.disks.'.$diskName.'.endpoint'), 'credentials' => [ - 'key' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.key'), - 'secret' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.secret'), + 'key' => config('filesystems.disks.'.$diskName.'.key'), + 'secret' => config('filesystems.disks.'.$diskName.'.secret'), ], ]; diff --git a/app/Jobs/ArchiveStudy.php b/app/Jobs/ArchiveStudy.php index 7cb918a86..5f9778afe 100644 --- a/app/Jobs/ArchiveStudy.php +++ b/app/Jobs/ArchiveStudy.php @@ -6,7 +6,7 @@ use App\Models\Project; use Aws\S3\S3Client; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldBeUnique; +use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; @@ -15,16 +15,23 @@ use Illuminate\Support\Facades\Storage; use ZipStream; -class ArchiveStudy implements ShouldBeUnique, ShouldQueue +class ArchiveStudy implements ShouldBeUniqueUntilProcessing, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 0; + /** + * Only ever run an archive job once. Failures must surface to Horizon + * rather than be retried, otherwise large zips re-run from scratch and + * compound the load on Ceph. + */ + public $tries = 1; + /** * The project instance. * - * @var \App\Models\Project + * @var Project */ public $project; @@ -44,22 +51,52 @@ public function uniqueId(): string return $this->project->id; } + /** + * Bound the unique lock so a crashed worker cannot strand it in Redis. + * The lock is also released as soon as `handle()` starts (per + * ShouldBeUniqueUntilProcessing), so the only role of this TTL is the + * crash/kill safety net. + */ + public function uniqueFor(): int + { + return 600; + } + /** * Execute the job. */ public function handle(): void { + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_study_job_started', + 'project_id' => $this->project->id, + ]); + // Debug logging - immediate write to log file Log::info('=== ArchiveStudy job STARTED for project: '.$this->project->id.' ==='); Log::info('Project ID: '.$this->project->id); Log::info('Current timestamp: '.now()->toString()); Log::info('Archiving study for projects '.$this->project->id); - $project = $this->project; + // Re-fetch the project so we see studies / files added between + // dispatch and handle. Without this, a job dispatched while a prior + // ArchiveStudy run was still working would observe stale state. + $project = Project::with('studies')->find($this->project->id); if ($project) { Log::info("Project {$project->id} studies count: ".$project->studies->count()); + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_study_project_loaded', + 'project_id' => $project->id, + 'studies_count' => $project->studies->count(), + ]); + foreach ($project->studies as $study) { Log::info("Processing study {$study->id}"); + // Refresh study state. The earlier reference held by `$project` + // could be stale if another job (or the FileSystemObject + // observer) updated `download_url` after we were enqueued. + $study->refresh(); $study->internal_status = 'processing'; $study->save(); $archiveDownloadURL = $study->download_url; @@ -84,7 +121,7 @@ public function handle(): void Log::info("Study {$study->id}: Using filesystem driver: {$filesystemDriver}, bucket: {$bucket}"); $s3keys = []; - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $relative_URL = $fsObject->relative_url; if ($fsObject->type == 'file') { Log::info("Study {$study->id}: Processing single file"); @@ -225,6 +262,12 @@ public function handle(): void $s3Url = $this->generateS3Url($zipFilePath, $filesystemDriver, $bucket); $study->download_url = $s3Url; Log::info("Study {$study->id}: Archive created successfully at: {$s3Url}"); + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_study_zip_built', + 'project_id' => $project->id, + 'study_id' => $study->id, + ]); } catch (\Exception $e) { // Clean up the partial file if it exists if (Storage::disk($filesystemDriver)->exists($zipFilePath)) { @@ -242,20 +285,44 @@ public function handle(): void $study->save(); } else { Log::info("Study {$study->id}: No fsObject found, marking complete"); + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_study_skip_no_fs_object', + 'project_id' => $project->id, + 'study_id' => $study->id, + ]); + $study->internal_status = 'complete'; $study->save(); } } else { Log::info("Study {$study->id}: Already has download URL, marking complete"); + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_study_skip_existing_download_url', + 'project_id' => $project->id, + 'study_id' => $study->id, + ]); + $study->internal_status = 'complete'; $study->save(); } } } else { Log::info('No project found'); + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_study_project_not_found', + 'project_id' => $this->project->id, + ]); } Log::info('=== ArchiveStudy job COMPLETED for project: '.$this->project->id.' ==='); + + Log::info('embargo_publish_trace', [ + 'stage' => 'archive_study_job_finished', + 'project_id' => $this->project->id, + ]); } /** @@ -298,7 +365,7 @@ private function generateS3Url(string $filePath, string $filesystemDriver, strin // protected function standardizeMolecule($mol) // { - // $response = Http::post('https://api.cheminf.studio/latest/chem/standardize', $mol); + // $response = Http::post(config('services.chemistry_standardize.url'), $mol); // return $response->json(); // } @@ -318,19 +385,20 @@ private function generateS3Url(string $filePath, string $filesystemDriver, strin /** * Get the S3 storage client instance. * - * @return \Aws\S3\S3Client + * @return S3Client */ protected function storageClient() { + $diskName = config('filesystems.default'); $config = [ - 'region' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.region'), + 'region' => config('filesystems.disks.'.$diskName.'.region'), 'version' => 'latest', 'use_path_style_endpoint' => true, - 'url' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.endpoint'), - 'endpoint' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.endpoint'), + 'url' => config('filesystems.disks.'.$diskName.'.endpoint'), + 'endpoint' => config('filesystems.disks.'.$diskName.'.endpoint'), 'credentials' => [ - 'key' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.key'), - 'secret' => config('filesystems.disks.'.env('FILESYSTEM_DRIVER').'.secret'), + 'key' => config('filesystems.disks.'.$diskName.'.key'), + 'secret' => config('filesystems.disks.'.$diskName.'.secret'), ], ]; diff --git a/app/Jobs/DataBackupJob.php b/app/Jobs/DataBackupJob.php index bf6610da3..a612674a3 100644 --- a/app/Jobs/DataBackupJob.php +++ b/app/Jobs/DataBackupJob.php @@ -30,9 +30,9 @@ public function handle(): void Artisan::call('backup:run --only-db'); // Define the bucket name and prefix. - $bucket = env('AWS_BUCKET'); - $prefix = env('APP_ENV').'/database'; - $disk = Storage::disk(env('FILESYSTEM_DRIVER')); + $bucket = config('filesystems.disks.ceph.bucket'); + $prefix = config('app.env').'/database'; + $disk = Storage::disk(config('filesystems.default')); // Get the contents of the bucket with the specified prefix. $contents = $disk->listContents($prefix, false); @@ -46,9 +46,9 @@ public function handle(): void // Set the visibility to public $disk->setVisibility($latestFile['path'], 'public'); - } - // Print the URL of the latest file. - echo 'Download the file from URL '.$latestFile['path']; + // Print the URL of the latest file. + echo 'Download the file from URL '.$latestFile['path']; + } } } diff --git a/app/Jobs/DeleteCitations.php b/app/Jobs/DeleteCitations.php index edea5f7d4..75939f2ab 100644 --- a/app/Jobs/DeleteCitations.php +++ b/app/Jobs/DeleteCitations.php @@ -36,7 +36,8 @@ public function __construct($citations) public function handle(): void { foreach ($this->citations as $citation) { - if (! DB::table('citation_project')->where('citation_id', $citation->id)->exists()) { + if (! DB::table('citation_project')->where('citation_id', $citation->id)->exists() + && ! DB::table('citation_study')->where('citation_id', $citation->id)->exists()) { $citation->delete(); } } diff --git a/app/Jobs/DeleteProjects.php b/app/Jobs/DeleteProjects.php index 18da96375..99c756ff0 100644 --- a/app/Jobs/DeleteProjects.php +++ b/app/Jobs/DeleteProjects.php @@ -18,7 +18,7 @@ class DeleteProjects implements ShouldQueue /** * The project instance. * - * @var \App\Models\Project + * @var Project */ private $project; @@ -40,9 +40,9 @@ public function handle(DeleteProject $deleteProject): void $project = $this->project; $deletedOn = $project->deleted_on; $diffInDays = null; - $coolOffPeriod = (int) env('COOL_OFF_PERIOD', '30'); + $coolOffPeriod = config('nmrxiv.cool_off_period'); if ($deletedOn) { - $diffInDays = Carbon::parse($deletedOn)->diffInDays(Carbon::now()); + $diffInDays = (int) Carbon::parse($deletedOn)->diffInDays(Carbon::now()); // Sending reminder to user 1 week and 1 day before. if ($diffInDays == ($coolOffPeriod - 7) || $diffInDays == ($coolOffPeriod - 1)) { $project->sendNotification('deletionReminder', $this->prepareSendList($project)); @@ -61,15 +61,19 @@ public function handle(DeleteProject $deleteProject): void */ public function prepareSendList($project) { - $sendTo = []; - foreach ($project->allUsers() as $member) { - if ($member->projectMembership->role == 'creator' || $member->projectMembership->role == 'owner') { - array_push($sendTo, $member); - } else { - array_push($sendTo, $project->owner); + $sendTo = collect(); + + if ($project->owner) { + $sendTo->push($project->owner); + } + + foreach ($project->users as $member) { + $role = $member->projectMembership?->role; + if ($role === 'creator' || $role === 'owner') { + $sendTo->push($member); } } - return $sendTo; + return $sendTo->unique('id')->values()->all(); } } diff --git a/app/Jobs/ProcessDraftELNSubmission.php b/app/Jobs/ProcessDraftELNSubmission.php index 5c2b0b221..08e63e41d 100644 --- a/app/Jobs/ProcessDraftELNSubmission.php +++ b/app/Jobs/ProcessDraftELNSubmission.php @@ -2,20 +2,23 @@ namespace App\Jobs; +use App\Actions\Author\SyncProjectAuthors; +use App\Actions\Citation\SyncCitations; use App\Actions\Draft\DraftProcessingLogger; use App\Actions\Draft\ProcessDraft; use App\Http\Controllers\FileSystemController; +use App\Models\Author; use App\Models\Draft; use App\Models\FileSystemObject; use App\Models\License; use App\Models\Molecule; use App\Models\Sample; -use App\Services\AuthorService; use App\Services\ChemotionRepositoryTrackerService; use App\Services\ELNMetadataServiceFactory; use App\Services\FileSystemObjectService; use App\Services\PathGeneratorService; use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Database\QueryException; use Illuminate\Foundation\Queue\Queueable; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Http; @@ -54,7 +57,8 @@ public function handle( try { $logger->log($draft, 'info', 'Starting ELN submission processing'); - if (strtolower($draft->eln) !== 'chemotion') { + // Check if ELN is supported + if (! in_array(strtolower($draft->eln), ['chemotion', 'nobs'])) { $logger->log($draft, 'info', "ELN not supported: {$draft->eln}"); return; @@ -293,6 +297,14 @@ private function processFolders(Draft $draft, FileSystemController $fileSystemCo } $logger->log($draft, 'info', 'Dispatching Archiving Jobs, Auto-Processing ELN Spectra, Validation And Submission Of ELN Draft'); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_draft_eln_submission_dispatch_chain', + 'project_id' => $project->id, + 'draft_id' => $draft->id, + 'chained_jobs' => ['ArchiveStudy', 'ProcessELNSpectra', 'ValidateAndSubmitELNDraft'], + ]); + ArchiveStudy::dispatch($project) ->chain([ new ProcessELNSpectra($project->id), @@ -364,47 +376,89 @@ private function attachMetadataToStudy($project, array $studyMetadata, DraftProc }) ->first(); + // If no direct match found, try to find studies within the sample folder + $studiesInSample = collect(); if (! $study) { - $logger->log($project->draft, 'error', 'Study not found: '.$studyName); + $logger->log($project->draft, 'info', 'Direct study not found. Looking for studies within sample folder: '.$studyName); + + // Find all studies whose name starts with the analysis folders in this sample + // The folder structure is: sample_X/analysis_Y/dataset_Z + // Studies are created with names like "dataset_Z" + $folderName = $studyMetadata['folderName'] ?? $studyName; + + // Get analysis IDs from the metadata datasets + $analysisIds = []; + if (isset($studyMetadata['chemical_substance']['datasets'])) { + foreach ($studyMetadata['chemical_substance']['datasets'] as $dataset) { + if (isset($dataset['analyses'])) { + $analysisIds[] = $dataset['analyses']; + } + if (isset($dataset['datasets']) && is_array($dataset['datasets'])) { + foreach ($dataset['datasets'] as $datasetId) { + $studiesInSample->push($project->studies()->where('name', $datasetId)->first()); + } + } + } + } - return; + $studiesInSample = $studiesInSample->filter(); + + if ($studiesInSample->isEmpty()) { + $logger->log($project->draft, 'error', 'No studies found for sample folder: '.$studyName); + + return; + } + + $logger->log($project->draft, 'info', 'Found '.$studiesInSample->count().' studies in sample folder'); + } else { + $studiesInSample = collect([$study]); } - // Get the draft to access processing logs - $draft = $project->draft; - $processingLogs = $draft ? $draft->process_logs : []; + // Process metadata for each study in the sample + foreach ($studiesInSample as $study) { + if (! $study) { + continue; + } - $study->update([ - 'name' => $studyMetadata['name'].' ('.$studyName.')', - 'external_url' => $studyMetadata['url'], - 'tracking_item_name' => $studyMetadata['tracking_item_name'], - 'processing_logs' => $processingLogs, - ]); + // Get the draft to access processing logs + $draft = $project->draft; + $processingLogs = $draft ? $draft->processing_logs : []; + + $study->update([ + 'name' => $studyMetadata['name'].' ('.$study->name.')', + 'external_url' => $studyMetadata['url'], + 'tracking_item_name' => $studyMetadata['tracking_item_name'], + 'processing_logs' => $processingLogs, + ]); - // update STATUS_PROCESSED in Chemotion Repository Tracker - $trackerService = app(ChemotionRepositoryTrackerService::class); - $trackerService->updateElnSubmissionStatus( - submissionId: $study->tracking_item_name, - newStatus: ChemotionRepositoryTrackerService::STATUS_PROCESSED, - additionalMetadata: $studyMetadata, - ownerName: $study->owner->first_name.' '.$study->owner->last_name, - ownerEmail: $study->owner->email - ); + $logger->log($project->draft, 'info', 'Attaching metadata to study: '.$study->name); - $logger->log($project->draft, 'info', 'Attaching metadata to study: '.$study->name); + $this->updateStudyDescription($study, $studyMetadata, $logger); - $this->updateStudyDescription($study, $studyMetadata, $logger); + $this->attachLicenseToStudy($study, $studyMetadata, $logger); - $this->attachLicenseToStudy($study, $studyMetadata, $logger); + $this->attachKeywordsToStudy($study, $studyMetadata, $logger); - $this->attachKeywordsToStudy($study, $studyMetadata, $logger); + $this->attachAuthorsToStudy($study, $studyMetadata['authors'], $logger); - $this->attachAuthorsToStudy($study, $studyMetadata['authors'], $logger); + $this->attachCitationsToStudy($study, $studyMetadata['citation'] ?? [], $logger); - // $this->attachCitationsToStudy($study, $studyMetadata['citation'] ?? [], $logger); + if (isset($studyMetadata['chemical_substance']['molecule'])) { + $this->attachMoleculesToStudy($study, [$studyMetadata['chemical_substance']['molecule']], $logger); + } + } - if (isset($studyMetadata['chemical_substance']['molecule'])) { - $this->attachMoleculesToStudy($study, [$studyMetadata['chemical_substance']['molecule']], $logger); + // update STATUS_PROCESSED in Chemotion Repository Tracker (only once for the first study) + if ($studiesInSample->isNotEmpty()) { + $firstStudy = $studiesInSample->first(); + $trackerService = app(ChemotionRepositoryTrackerService::class); + $trackerService->updateElnSubmissionStatus( + submissionId: $firstStudy->tracking_item_name, + newStatus: ChemotionRepositoryTrackerService::STATUS_PROCESSED, + additionalMetadata: $studyMetadata, + ownerName: $firstStudy->owner->first_name.' '.$firstStudy->owner->last_name, + ownerEmail: $firstStudy->owner->email + ); } } catch (\Exception $e) { @@ -450,8 +504,11 @@ private function attachAuthorsToStudy($study, array $authors, DraftProcessingLog } if (! empty($authorData)) { - $authorService = app(AuthorService::class); - $authorService->syncAuthors($project, $authorData); + // Sync authors to project + app(SyncProjectAuthors::class)->handle($project, $authorData); + + // Also sync authors directly to the study for independent studies + $this->syncStudyAuthors($study, $authorData, $logger); } } catch (\Exception $e) { @@ -459,31 +516,80 @@ private function attachAuthorsToStudy($study, array $authors, DraftProcessingLog } } + /** + * Sync authors to study. + */ + private function syncStudyAuthors($study, array $authorData, DraftProcessingLogger $logger): void + { + try { + $authorIds = []; + + foreach ($authorData as $index => $data) { + // Find existing author by ORCID or name + $author = null; + + if (! empty($data['orcid_id'])) { + $author = Author::where('orcid_id', $data['orcid_id'])->first(); + } + + if (! $author) { + $author = Author::where('given_name', $data['given_name']) + ->where('family_name', $data['family_name']) + ->first(); + } + + // Create if not found + if (! $author) { + $author = Author::create([ + 'given_name' => $data['given_name'], + 'family_name' => $data['family_name'], + 'email_id' => $data['email_id'], + 'orcid_id' => $data['orcid_id'], + 'affiliation' => $data['affiliation'], + ]); + } + + $authorIds[$author->id] = [ + 'contributor_type' => $data['contributor_type'], + 'sort_order' => $index + 1, + ]; + } + + // Sync authors to study + $study->studyAuthors()->sync($authorIds); + + $logger->log($study->project->draft, 'info', 'Synced '.count($authorIds).' authors to study: '.$study->name); + + } catch (\Exception $e) { + $logger->log($study->project->draft, 'error', 'Failed to sync authors to study: '.$e->getMessage()); + } + } + /** * Attach citations to study. */ - private function attachCitationsToStudy($study, array $citations): void + private function attachCitationsToStudy($study, array $citations, DraftProcessingLogger $logger): void { if (empty($citations)) { + $logger->log($study->project->draft, 'info', 'No citations found for study: '.$study->name); + return; } try { - // Store citations as JSON in study metadata $study->update([ - 'citations' => json_encode($citations), + 'citations' => $citations, ]); - Log::info('Attached citations to study', [ - 'study_id' => $study->id, - 'citations_count' => count($citations), - ]); + $ownerUser = $study->owner; + if ($ownerUser) { + app(SyncCitations::class)->syncFromStudyElnPayload($study, $citations, $ownerUser); + } + + $logger->log($study->project->draft, 'info', 'Attached '.count($citations).' citations to study: '.$study->name); } catch (\Exception $e) { - Log::error('Failed to attach citations to study', [ - 'study_id' => $study->id, - 'error' => $e->getMessage(), - ]); + $logger->log($study->project->draft, 'error', 'Failed to attach citations to study: '.$e->getMessage()); } } @@ -673,24 +779,56 @@ private function createOrFindMolecule(array $moleculeInfo, DraftProcessingLogger } if (! $molecule) { - $molecule = Molecule::create([ - 'molecular_formula' => $moleculeInfo['molecular_formula'] ?? null, - 'molecular_weight' => $moleculeInfo['molecular_weight'] ?? null, - 'smiles' => $moleculeInfo['smiles'] ?? null, - 'absolute_smiles' => $moleculeInfo['absolute_smiles'] ?? null, - 'canonical_smiles' => $moleculeInfo['canonical_smiles'] ?? $moleculeInfo['smiles'] ?? null, - 'inchi' => $moleculeInfo['inchi'] ?? null, - 'standard_inchi' => $moleculeInfo['standard_inchi'] ?? $moleculeInfo['inchi'] ?? null, - 'inchi_key' => $moleculeInfo['inchi_key'] ?? null, - 'standard_inchi_key' => $moleculeInfo['standard_inchi_key'] ?? $moleculeInfo['inchi_key'] ?? null, - ]); + try { + $molecule = DB::transaction(function () use ($moleculeInfo) { + return Molecule::create([ + 'molecular_formula' => $moleculeInfo['molecular_formula'] ?? null, + 'molecular_weight' => $moleculeInfo['molecular_weight'] ?? null, + 'smiles' => $moleculeInfo['smiles'] ?? null, + 'absolute_smiles' => $moleculeInfo['absolute_smiles'] ?? null, + 'canonical_smiles' => $moleculeInfo['canonical_smiles'] ?? $moleculeInfo['smiles'] ?? null, + 'inchi' => $moleculeInfo['inchi'] ?? null, + 'standard_inchi' => $moleculeInfo['standard_inchi'] ?? $moleculeInfo['inchi'] ?? null, + 'inchi_key' => $moleculeInfo['inchi_key'] ?? null, + 'standard_inchi_key' => $moleculeInfo['standard_inchi_key'] ?? $moleculeInfo['inchi_key'] ?? null, + ]); + }); - Log::info('Created new molecule', [ - 'molecule_id' => $molecule->id, - 'molecular_formula' => $molecule->molecular_formula, - 'inchi_key' => $molecule->inchi_key, - ]); + Log::info('Created new molecule', [ + 'molecule_id' => $molecule->id, + 'molecular_formula' => $molecule->molecular_formula, + 'inchi_key' => $molecule->inchi_key, + ]); + } catch (QueryException $e) { + $standardInchi = $moleculeInfo['standard_inchi'] ?? $moleculeInfo['inchi'] ?? null; + + if (! empty($moleculeInfo['inchi'])) { + $molecule = Molecule::where('inchi', $moleculeInfo['inchi'])->first(); + } + + if (! $molecule && ! empty($moleculeInfo['smiles'])) { + $molecule = Molecule::where('smiles', $moleculeInfo['smiles'])->first(); + } + if (! $molecule && $standardInchi) { + $molecule = Molecule::where('standard_inchi', $standardInchi)->first(); + } + + if (! $molecule) { + Log::error('Failed to create or find molecule', [ + 'molecule_info' => $moleculeInfo, + 'error' => $e->getMessage(), + ]); + + return null; + } + + Log::info('Found existing molecule', [ + 'molecule_id' => $molecule->id, + 'molecular_formula' => $molecule->molecular_formula, + 'inchi_key' => $molecule->inchi_key, + ]); + } } else { Log::info('Found existing molecule', [ 'molecule_id' => $molecule->id, diff --git a/app/Jobs/ProcessELNSpectra.php b/app/Jobs/ProcessELNSpectra.php index 7c73d8b8b..09e04e25e 100644 --- a/app/Jobs/ProcessELNSpectra.php +++ b/app/Jobs/ProcessELNSpectra.php @@ -29,18 +29,42 @@ public function handle(): void if (! $project) { Log::error("Project not found: {$this->projectId}"); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_eln_spectra_aborted', + 'reason' => 'project_not_found', + 'project_id' => $this->projectId, + ]); + return; } try { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_eln_spectra_start', + 'project_id' => $project->id, + 'identifier' => $project->identifier, + ]); + Log::info("Starting spectra processing for project: {$project->identifier}"); $this->processProjectStudies($project); Log::info("Successfully completed spectra processing for project: {$project->identifier}"); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_eln_spectra_complete', + 'project_id' => $project->id, + ]); + } catch (Exception $e) { Log::error("Failed to process spectra for project {$project->identifier}: ".$e->getMessage()); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_eln_spectra_failed', + 'project_id' => $project->id, + 'error' => $e->getMessage(), + ]); + throw $e; } } diff --git a/app/Jobs/ProcessFiles.php b/app/Jobs/ProcessFiles.php index f3e4cae92..a43ca4371 100644 --- a/app/Jobs/ProcessFiles.php +++ b/app/Jobs/ProcessFiles.php @@ -132,6 +132,12 @@ private function handleMissingFilesRestored(): void $study->update(['download_url' => null]); } + Log::info('embargo_publish_trace', [ + 'stage' => 'process_files_dispatch_archive_study', + 'project_id' => $project->id, + 'draft_id' => $this->draft->id, + ]); + // Dispatch archive job to regenerate archives ArchiveStudy::dispatch($project); } diff --git a/app/Jobs/ProcessMetadataExtractionBagitGenerationJob.php b/app/Jobs/ProcessMetadataExtractionBagitGenerationJob.php new file mode 100644 index 000000000..d71c923e2 --- /dev/null +++ b/app/Jobs/ProcessMetadataExtractionBagitGenerationJob.php @@ -0,0 +1,475 @@ +tries = config('nmrxiv.spectra_parsing.job_tries', 3); + $this->timeout = config('nmrxiv.spectra_parsing.job_timeout', 600); + $this->retries = config('nmrxiv.spectra_parsing.retry_count', 3); + } + + /** + * Execute the job. + */ + public function handle(): void + { + try { + $study = Study::with('datasets')->find($this->studyId); + + if (! $study) { + throw new \Exception("Study {$this->studyId} not found"); + } + + // Mark as processing + $study->update([ + 'metadata_bagit_generation_status' => 'processing', + 'metadata_bagit_generation_logs' => array_merge((array) ($study->metadata_bagit_generation_logs ?: []), [ + 'started_at' => now()->toIso8601String(), + ]), + ]); + + Log::info("Processing metadata extraction for study {$study->id} ({$study->identifier})"); + + // Process the study with BagIt structure + $result = $this->processStudy($study); + + // Mark as completed with metadata + $study->update([ + 'metadata_bagit_generation_status' => 'completed', + 'metadata_bagit_generation_logs' => array_merge((array) ($study->metadata_bagit_generation_logs ?: []), [ + 'completed_at' => now()->toIso8601String(), + 'storage_path' => $result['location'], + 'image_count' => $result['imageCount'], + ]), + ]); + + Log::info("Successfully processed study {$study->id} ({$study->identifier}): {$result['imageCount']} images saved to {$result['location']}"); + } catch (\Exception $e) { + Log::error("Failed to process study {$this->studyId}: {$e->getMessage()}"); + + // Mark as failed with error message + $study = Study::find($this->studyId); + if ($study) { + $study->update([ + 'metadata_bagit_generation_status' => 'failed', + 'metadata_bagit_generation_logs' => array_merge((array) ($study->metadata_bagit_generation_logs ?: []), [ + 'failed_at' => now()->toIso8601String(), + 'error_message' => $e->getMessage(), + ]), + ]); + } + + // Don't rethrow - let the job complete so it doesn't retry infinitely + } + } + + /** + * Process a single study with BagIt structure. + */ + protected function processStudy(Study $study): array + { + // Remove NMRXIV: prefix if present (e.g., "NMRXIV:S1295" -> "S1295") + $studyIdentifier = str_replace('NMRXIV:', '', $study->identifier); + $disk = Storage::disk(config('nmrxiv.spectra_parsing.storage_disk', 'local')); + $basePath = config('nmrxiv.spectra_parsing.storage_path', 'spectra_parse'); + $baseDir = "{$basePath}/{$studyIdentifier}"; + $dataDir = "{$baseDir}/data"; + $zipPath = null; + + try { + // Step 1: Download ZIP file + Log::info("Step 1/7: Downloading ZIP file for study {$study->id}"); + $zipPath = $this->downloadWithRetry($study->download_url, $this->retries); + + // Step 2: Extract ZIP to data directory + Log::info('Step 2/7: Extracting ZIP archive...'); + $studyName = $this->extractZip($zipPath, $disk->path($dataDir)); + + // Step 3: Call NMRKit API + Log::info('Step 3/7: Calling NMRKit API...'); + $jsonData = $this->callNMRKitAPI($study->download_url, $this->retries); + + // Step 4: Fetch Bio-Schema + Log::info('Step 4/7: Fetching bio-schema...'); + $bioSchema = null; + try { + $bioSchema = $this->fetchBioSchema($studyIdentifier, $this->retries); + } catch (\Exception $e) { + Log::warning("Bio-schema fetch failed: {$e->getMessage()}. Continuing without bio-schema..."); + } + + // Step 5: Create nmrxiv-meta structure + Log::info('Step 5/7: Creating nmrxiv-meta structure...'); + $metaDir = "{$dataDir}/{$studyName}/nmrxiv-meta"; + $imagesDir = "{$metaDir}/images"; + + if (! $disk->exists($metaDir)) { + $disk->makeDirectory($metaDir, 0755, true); + } + + // Clean up old images directory to prevent duplicates from previous runs + if ($disk->exists($imagesDir)) { + // Delete all PNG files in the images directory + $oldImages = $disk->files($imagesDir); + foreach ($oldImages as $oldImage) { + $disk->delete($oldImage); + } + Log::info('Cleaned up '.count($oldImages).' old image files'); + } else { + $disk->makeDirectory($imagesDir, 0755, true); + } + + // Clean up spectra data + if (isset($jsonData['data']['spectra']) && is_array($jsonData['data']['spectra'])) { + foreach ($jsonData['data']['spectra'] as &$spectra) { + unset($spectra['data']); + unset($spectra['meta']); + unset($spectra['originalData']); + unset($spectra['originalInfo']); + } + unset($spectra); + } + + // Extract and save images as PNG files + $imageCount = 0; + if (isset($jsonData['images']) && is_array($jsonData['images'])) { + foreach ($jsonData['images'] as $imageData) { + if (isset($imageData['id']) && isset($imageData['image'])) { + $imageId = $imageData['id']; + $base64Data = $imageData['image']; + + // Save PNG file + $pngPath = "{$imagesDir}/{$imageId}.png"; + $this->savePNGFromBase64($base64Data, $disk->path($pngPath)); + $imageCount++; + } + } + } + + // Save S{identifier}.nmrium (full API response with base64 images intact) + $nmriumPath = "{$metaDir}/{$studyIdentifier}.nmrium"; + $formattedJson = json_encode($jsonData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + $disk->put($nmriumPath, $formattedJson); + + // Save bio-schema.json + if ($bioSchema !== null) { + $bioSchemaPath = "{$metaDir}/bio-schema.json"; + $bioSchemaJson = json_encode($bioSchema, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + $disk->put($bioSchemaPath, $bioSchemaJson); + } + + // Step 6: Generate BagIt manifests + Log::info('Step 6/7: Generating BagIt manifests...'); + $this->generateBagItManifests($disk->path($baseDir)); + + return [ + 'imageCount' => $imageCount, + 'location' => $disk->path($baseDir), + ]; + } finally { + // Step 7: Cleanup temporary files (always runs, even on exception) + if ($zipPath && file_exists($zipPath)) { + Log::info('Step 7/7: Cleaning up temporary ZIP file...'); + @unlink($zipPath); + } + } + } + + /** + * Download file with retry logic. + */ + protected function downloadWithRetry(string $url, int $retries): string + { + $attempt = 0; + $lastException = null; + + while ($attempt < $retries) { + try { + $attempt++; + Log::debug("Download attempt {$attempt}/{$retries}..."); + + $tempPath = storage_path('app/temp_'.uniqid().'.zip'); + $timeout = config('nmrxiv.spectra_parsing.download_timeout', 300); + $response = Http::timeout($timeout)->get($url); + + if (! $response->successful()) { + throw new \Exception("Download failed with status {$response->status()}"); + } + + file_put_contents($tempPath, $response->body()); + + return $tempPath; + } catch (\Exception $e) { + $lastException = $e; + if ($attempt < $retries) { + Log::warning("Download failed: {$e->getMessage()}. Retrying..."); + sleep(2); + } + } + } + + throw new \Exception("Download failed after {$retries} attempts: ".$lastException->getMessage()); + } + + /** + * Extract ZIP file and return the study name. + */ + protected function extractZip(string $zipPath, string $extractTo): string + { + $zip = new ZipArchive; + + if ($zip->open($zipPath) !== true) { + throw new \Exception("Failed to open ZIP file: {$zipPath}"); + } + + // Get the root folder name from first entry + $studyName = null; + if ($zip->numFiles > 0) { + $firstEntry = $zip->getNameIndex(0); + $parts = explode('/', $firstEntry); + $studyName = $parts[0]; + } + + if (! $studyName) { + throw new \Exception('Could not determine study name from ZIP'); + } + + // Extract all files + $zip->extractTo($extractTo); + $zip->close(); + + return $studyName; + } + + /** + * Call NMRKit API with retry logic. + */ + protected function callNMRKitAPI(string $url, int $retries): array + { + $attempt = 0; + $lastException = null; + + while ($attempt < $retries) { + try { + $attempt++; + Log::debug("NMRKit API attempt {$attempt}/{$retries}..."); + + $timeout = config('nmrxiv.spectra_parsing.api_timeout', 300); + $apiUrl = config('nmrxiv.spectra_parsing.nmrkit_api_url'); + + $response = Http::timeout($timeout) + ->post($apiUrl, [ + 'url' => $url, + 'capture_snapshot' => true, + 'auto_processing' => true, + 'auto_detection' => true, + ]); + + if (! $response->successful()) { + throw new \Exception("API request failed with status {$response->status()}: {$response->body()}"); + } + + return $response->json(); + } catch (\Exception $e) { + $lastException = $e; + if ($attempt < $retries) { + Log::warning("API call failed: {$e->getMessage()}. Retrying..."); + sleep(2); + } + } + } + + throw new \Exception("API call failed after {$retries} attempts: ".$lastException->getMessage()); + } + + /** + * Fetch bio-schema from nmrxiv.org API with retry logic. + */ + protected function fetchBioSchema(string $studyIdentifier, int $retries): array + { + $attempt = 0; + $lastException = null; + $baseUrl = config('nmrxiv.spectra_parsing.bioschema_api_url'); + $url = "{$baseUrl}/{$studyIdentifier}"; + + while ($attempt < $retries) { + try { + $attempt++; + Log::debug("Bio-schema attempt {$attempt}/{$retries}..."); + + $response = Http::timeout(60)->get($url); + + if (! $response->successful()) { + throw new \Exception("Bio-schema request failed with status {$response->status()}"); + } + + return $response->json(); + } catch (\Exception $e) { + $lastException = $e; + if ($attempt < $retries) { + Log::warning("Bio-schema fetch failed: {$e->getMessage()}. Retrying..."); + sleep(2); + } + } + } + + throw new \Exception("Bio-schema fetch failed after {$retries} attempts: ".$lastException->getMessage()); + } + + /** + * Save PNG image from base64 data. + */ + protected function savePNGFromBase64(string $base64Data, string $outputPath): void + { + // Remove data:image/png;base64, prefix if present + $base64Data = preg_replace('/^data:image\/[a-z]+;base64,/', '', $base64Data); + + $imageData = base64_decode($base64Data); + + if ($imageData === false) { + throw new \Exception('Failed to decode base64 image data'); + } + + file_put_contents($outputPath, $imageData); + } + + /** + * Generate BagIt manifests using whikloj/BagItTools library. + */ + protected function generateBagItManifests(string $bagPath): void + { + try { + // Create bag using BagItTools library + $bag = Bag::create($bagPath); + + // Update bag with checksums + $bag->update(); + + // Package the bag (this generates manifests) + $bag->package($bagPath); + + Log::debug('Used BagItTools library for manifest generation'); + } catch (\Exception $e) { + Log::warning("BagIt library failed: {$e->getMessage()}. Falling back to manual generation..."); + + // Fallback: Generate manually + $this->generateBagItManually($bagPath); + } + } + + /** + * Manually generate BagIt manifests. + */ + protected function generateBagItManually(string $bagPath): void + { + // Create bagit.txt + $bagitContent = "BagIt-Version: 1.0\nTag-File-Character-Encoding: UTF-8\n"; + file_put_contents($bagPath.'/bagit.txt', $bagitContent); + + // Create manifest-sha256.txt + $manifestLines = []; + $dataPath = $bagPath.'/data'; + + if (is_dir($dataPath)) { + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dataPath), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $file) { + if ($file->isFile()) { + $relativePath = str_replace($bagPath.'/', '', $file->getPathname()); + $hash = hash_file('sha256', $file->getPathname()); + $manifestLines[] = "{$hash} {$relativePath}"; + } + } + } + + file_put_contents($bagPath.'/manifest-sha256.txt', implode("\n", $manifestLines)."\n"); + + // Create bag-info.txt + $bagInfoContent = 'Payload-Oxum: '.$this->calculatePayloadOxum($dataPath)."\n"; + $bagInfoContent .= 'Bagging-Date: '.date('Y-m-d')."\n"; + $bagInfoContent .= "Bag-Software-Agent: Laravel-Queue-ProcessStudySpectraJob/1.0\n"; + file_put_contents($bagPath.'/bag-info.txt', $bagInfoContent); + + // Create tagmanifest-sha256.txt + $tagManifestLines = []; + foreach (['bagit.txt', 'bag-info.txt', 'manifest-sha256.txt'] as $tagFile) { + $tagFilePath = $bagPath.'/'.$tagFile; + if (file_exists($tagFilePath)) { + $hash = hash_file('sha256', $tagFilePath); + $tagManifestLines[] = "{$hash} {$tagFile}"; + } + } + file_put_contents($bagPath.'/tagmanifest-sha256.txt', implode("\n", $tagManifestLines)."\n"); + + Log::debug('Manual BagIt generation complete'); + } + + /** + * Calculate Payload-Oxum (total bytes.total files). + */ + protected function calculatePayloadOxum(string $dataPath): string + { + $totalBytes = 0; + $totalFiles = 0; + + if (is_dir($dataPath)) { + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dataPath), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $file) { + if ($file->isFile()) { + $totalBytes += $file->getSize(); + $totalFiles++; + } + } + } + + return "{$totalBytes}.{$totalFiles}"; + } +} diff --git a/app/Jobs/ProcessProject.php b/app/Jobs/ProcessProject.php index 960f6da14..0ac65e5d7 100644 --- a/app/Jobs/ProcessProject.php +++ b/app/Jobs/ProcessProject.php @@ -8,6 +8,7 @@ use App\Models\FileSystemObject; use App\Models\Project; use App\Notifications\DraftProcessedNotification; +use App\Services\DOI\DOIService; use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; @@ -15,6 +16,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Storage; @@ -27,7 +29,7 @@ class ProcessProject implements ShouldBeUnique, ShouldQueue /** * The project instance. * - * @var \App\Models\Project + * @var Project */ public $project; @@ -48,6 +50,13 @@ public function handle(AssignIdentifier $assigner, UpdateDOI $updater, PublishPr { $project = $this->project; + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_start', + 'project_id' => $project->id, + 'status' => $project->status, + 'release_date' => filled($project->release_date) ? Carbon::parse($project->release_date)->toIso8601String() : null, + ]); + $project->status = 'processing'; $project->save(); @@ -58,7 +67,7 @@ public function handle(AssignIdentifier $assigner, UpdateDOI $updater, PublishPr $draft = $project->draft; if ($draft) { - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $projectPath = preg_replace( '~//+~', @@ -101,20 +110,89 @@ public function handle(AssignIdentifier $assigner, UpdateDOI $updater, PublishPr $project->save(); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_files_moved', + 'project_id' => $project->id, + 'had_draft' => $draft !== null, + ]); + $assigner->assign($project->fresh()); $release_date = Carbon::parse($project->release_date); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_release_check', + 'project_id' => $project->id, + 'release_is_past' => $release_date->isPast(), + ]); + if ($release_date->isPast()) { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_immediate_publish', + 'project_id' => $project->id, + ]); $publisher->publish($project); + } else { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_skip_publish_future_release', + 'project_id' => $project->id, + ]); } $updater->update($project->fresh()); - Notification::send($project->owner, new DraftProcessedNotification($project)); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_after_update_doi', + 'project_id' => $project->id, + ]); + + $this->linkProvisionalDoiSafely($project->fresh()); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_before_owner_notification', + 'project_id' => $project->id, + ]); + + Notification::send($project->owner, new DraftProcessedNotification($project->fresh())); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_project_complete', + 'project_id' => $project->id, + ]); } } - public function moveFolder($fsObject, $draft, $path) + /** + * @see ProcessSubmission::linkProvisionalDoiSafely + */ + private function linkProvisionalDoiSafely(Project $project): void + { + if (empty($project->provisional_doi) || empty($project->doi)) { + return; + } + + try { + $project->linkProvisionalDoi(app(DOIService::class)); + } catch (\Throwable $e) { + Log::warning('ProcessProject: linkProvisionalDoi failed; canonical DOI is still valid', [ + 'project_id' => $project->id, + 'doi' => $project->doi, + 'provisional_doi' => $project->provisional_doi, + 'error' => $e->getMessage(), + ]); + } + } + + /** + * @see ProcessSubmission::moveFolder() + */ + public function moveFolder($fsObject, $draft, $path): void + { + FileSystemObject::withoutEvents(function () use ($fsObject, $draft, $path): void { + $this->relocateFolderTreeDuringPublish($fsObject, $draft, $path); + }); + } + + private function relocateFolderTreeDuringPublish($fsObject, $draft, $path): void { $newPath = str_replace($draft->path, $path, $fsObject->path); $fsObject->path = $newPath; @@ -128,11 +206,11 @@ public function moveFolder($fsObject, $draft, $path) $path, $fsObjectChild->path ); - Storage::disk(env('FILESYSTEM_DRIVER'))->move($fsObjectChild->path, $newPath); + Storage::disk(config('filesystems.default'))->move($fsObjectChild->path, $newPath); $fsObjectChild->path = $newPath; $fsObjectChild->save(); } else { - $this->moveFolder($fsObjectChild, $draft, $path); + $this->relocateFolderTreeDuringPublish($fsObjectChild, $draft, $path); } } } diff --git a/app/Jobs/ProcessProjectSpectra.php b/app/Jobs/ProcessProjectSpectra.php deleted file mode 100644 index 7c73d8b8b..000000000 --- a/app/Jobs/ProcessProjectSpectra.php +++ /dev/null @@ -1,303 +0,0 @@ -projectId); - - if (! $project) { - Log::error("Project not found: {$this->projectId}"); - - return; - } - - try { - Log::info("Starting spectra processing for project: {$project->identifier}"); - - $this->processProjectStudies($project); - - Log::info("Successfully completed spectra processing for project: {$project->identifier}"); - - } catch (Exception $e) { - Log::error("Failed to process spectra for project {$project->identifier}: ".$e->getMessage()); - throw $e; - } - } - - /** - * Process all studies in the project. - */ - private function processProjectStudies(Project $project): void - { - $studies = $project->studies; - - foreach ($studies as $study) { - Log::info("Processing study: {$study->identifier}"); - - try { - // Process study-level NMRium data - $this->processStudySpectra($study); - - // Process dataset-level NMRium data - $this->processStudyDatasets($study->fresh()); - - } catch (Exception $e) { - Log::error("Failed to process study {$study->identifier}: ".$e->getMessage()); - - // Continue with other studies instead of failing the entire job - continue; - } - } - } - - /** - * Process spectra for a single study. - */ - private function processStudySpectra($study): void - { - if ($study->has_nmrium) { - return; // Already processed - } - - DB::transaction(function () use ($study) { - $downloadUrl = $study->download_url; - - if (! $downloadUrl) { - Log::warning("No download URL found for study: {$study->identifier}"); - - return; - } - - $nmriumData = $this->processSpectra($downloadUrl); - - if (! $nmriumData || ! isset($nmriumData['data'])) { - Log::warning("No valid spectra data returned for study: {$study->identifier}"); - - return; - } - - $parsedSpectra = $nmriumData['data']; - - // Clean up spectra data - foreach ($parsedSpectra['spectra'] as &$spectra) { - unset($spectra['data']); - unset($spectra['meta']); - unset($spectra['originalData']); - unset($spectra['originalInfo']); - } - - $version = $parsedSpectra['version'] ?? null; - unset($parsedSpectra['version']); - - $nmriumJSON = [ - 'data' => $parsedSpectra, - 'version' => $version, - ]; - - // Create or update NMRium record - $nmrium = $study->nmrium; - - if ($nmrium) { - $nmrium->nmrium_info = json_encode($nmriumJSON, JSON_UNESCAPED_UNICODE); - $nmrium->save(); - } else { - $nmrium = NMRium::create([ - 'nmrium_info' => json_encode($nmriumJSON, JSON_UNESCAPED_UNICODE), - ]); - $study->nmrium()->save($nmrium); - } - - $study->has_nmrium = true; - $study->save(); - - Log::info("Successfully processed study spectra: {$study->identifier}"); - }); - } - - /** - * Process datasets for a study. - */ - private function processStudyDatasets($study): void - { - if (! $study->has_nmrium) { - Log::warning("Study {$study->identifier} has no NMRium data, skipping datasets"); - - return; - } - - $nmriumInfo = json_decode($study->nmrium->nmrium_info, true); - - if (! isset($nmriumInfo['data']['spectra']) || count($nmriumInfo['data']['spectra']) == 0) { - Log::warning("Study {$study->identifier} has no spectra info, skipping datasets"); - - return; - } - - foreach ($study->datasets as $dataset) { - if ($dataset->has_nmrium) { - continue; // Already processed - } - - try { - $this->processDatasetSpectra($dataset, $study, $nmriumInfo); - } catch (Exception $e) { - Log::error("Failed to process dataset {$dataset->identifier}: ".$e->getMessage()); - - continue; - } - } - } - - /** - * Process spectra for a single dataset. - */ - private function processDatasetSpectra($dataset, $study, $nmriumInfo): void - { - $nmriumJSON = $nmriumInfo; - $fsObject = $dataset->fsObject; - $studyFSObject = $study->fsObject; - $datasetFSObject = $dataset->fsObject; - $draft = $study->draft; - - // Determine path based on ELN type - if ($draft && $draft->eln == 'chemotion') { - $path = '/'.$studyFSObject->name.'/'.$datasetFSObject->parent->name.'/'.$datasetFSObject->name; - } else { - $path = '/'.$studyFSObject->name.'/'.$datasetFSObject->name; - } - - $fType = $studyFSObject->type; - $matchingSpectra = []; - $types = []; - - // Find matching spectra for this dataset - foreach ($nmriumInfo['data']['spectra'] as $spectra) { - if ($this->spectraMatchesDataset($spectra, $path, $fType)) { - $matchingSpectra[] = $spectra; - $types[] = $this->extractSpectraType($spectra); - } - } - - if (count($matchingSpectra) > 0) { - // Create dataset-specific NMRium data - unset($nmriumJSON['data']['spectra']); - $nmriumJSON['data']['spectra'] = $matchingSpectra; - - // Create or update NMRium record for dataset - $nmrium = $dataset->nmrium; - - if ($nmrium) { - $nmrium->nmrium_info = json_encode($nmriumJSON, JSON_UNESCAPED_UNICODE); - $nmrium->save(); - } else { - $nmrium = NMRium::create([ - 'nmrium_info' => json_encode($nmriumJSON, JSON_UNESCAPED_UNICODE), - ]); - $dataset->nmrium()->save($nmrium); - } - - $dataset->has_nmrium = true; - - Log::info("Successfully processed dataset spectra: {$dataset->identifier}"); - } else { - Log::info("No matching spectra found for dataset: {$dataset->identifier}"); - } - - // Always update dataset type if unique (regardless of whether spectra were found) - $uniqueTypes = array_unique(array_filter($types)); - if (count($uniqueTypes) == 1) { - $dataset->type = $uniqueTypes[0]; - } - - $dataset->save(); - } - - /** - * Check if spectra matches the dataset path. - */ - private function spectraMatchesDataset($spectra, string $path, string $fType): bool - { - $files = $spectra['sourceSelector']['files'] ?? []; - - if (! $files) { - return false; - } - - foreach ($files as $file) { - $searchPath = $fType == 'file' ? $path : $path.'/'; - if (str_contains($file, $searchPath)) { - return true; - } - } - - return false; - } - - /** - * Extract spectra type from spectra info. - */ - private function extractSpectraType($spectra): ?string - { - if (! isset($spectra['info']['experiment'])) { - return null; - } - - $experiment = $spectra['info']['experiment']; - $nucleus = $spectra['info']['nucleus'] ?? null; - - if (is_array($nucleus)) { - $nucleus = implode('-', $nucleus); - } - - return $nucleus ? "{$experiment} - {$nucleus}" : $experiment; - } - - /** - * Process spectra using external service. - */ - private function processSpectra(string $url): ?array - { - try { - $encodedUrl = urlencode($url); - - $response = Http::timeout(300)->post('https://nodejs.nmrxiv.org/spectra-parser', [ - 'urls' => [$encodedUrl], - 'snapshot' => false, - ]); - - if (! $response->successful()) { - Log::error('Spectra processing service returned error: '.$response->status()); - - return null; - } - - return $response->json(); - - } catch (Exception $e) { - Log::error("Failed to process spectra from URL {$url}: ".$e->getMessage()); - - return null; - } - } -} diff --git a/app/Jobs/ProcessSubmission.php b/app/Jobs/ProcessSubmission.php index 160b5f894..254d64886 100644 --- a/app/Jobs/ProcessSubmission.php +++ b/app/Jobs/ProcessSubmission.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Actions\Citation\SyncCitationPivot; use App\Actions\Project\AssignIdentifier; use App\Actions\Project\PublishProject; use App\Actions\Project\UpdateDOI; @@ -10,6 +11,7 @@ use App\Models\FileSystemObject; use App\Models\Project; use App\Notifications\StudyPublishNotification; +use App\Services\DOI\DOIService; use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; @@ -17,6 +19,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Storage; @@ -27,7 +30,7 @@ class ProcessSubmission implements ShouldBeUnique, ShouldQueue /** * The project instance. * - * @var \App\Models\Project + * @var Project */ public $project; @@ -48,17 +51,54 @@ public function handle(AssignIdentifier $assigner, UpdateDOI $updater, PublishPr { $project = $this->project; + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_start', + 'project_id' => $project->id, + 'status' => $project->status, + 'release_date' => filled($project->release_date) ? Carbon::parse($project->release_date)->toIso8601String() : null, + 'draft_id' => $project->draft_id, + ]); + $project->status = 'processing'; $project->save(); $draft = $project->draft; + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_draft_loaded', + 'project_id' => $project->id, + 'draft_present' => $draft !== null, + 'draft_project_enabled' => $draft?->project_enabled, + ]); + + if ($draft === null) { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_missing_draft_republish_path', + 'project_id' => $project->id, + ]); + + $project = $project->fresh(); + + if (! $project || $project->studies()->doesntExist()) { + Log::warning('embargo_publish_trace', [ + 'stage' => 'process_submission_missing_draft_aborted', + 'project_id' => $this->project->id, + ]); + + return; + } + + $this->finalizeProjectModeFromReleaseDate($project, $projectPublisher, $assigner, $updater); + + return; + } + if ($draft->project_enabled) { $logs = 'Moving files in progress'; if ($project) { if ($draft) { - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); $projectPath = preg_replace( '~//+~', @@ -99,28 +139,26 @@ public function handle(AssignIdentifier $assigner, UpdateDOI $updater, PublishPr $project->draft_id = null; - $project->status = 'complete'; - - $project->save(); - - $assigner->assign($project->fresh()); - - $release_date = Carbon::parse($project->release_date); - - if ($release_date->isPast()) { - $projectPublisher->publish($project); - } - $updater->update($project->fresh()); - ArchiveProject::dispatch($project); - $project->sendNotification('publish', $this->prepareSendList($project)); + $this->finalizeProjectModeFromReleaseDate($project, $projectPublisher, $assigner, $updater); } } else { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_samples_mode_branch', + 'project_id' => $project->id, + ]); + $logs = 'Moving files in progress'; if ($project) { $_studies = $project->studies; if ($draft) { - $environment = env('APP_ENV', 'local'); + $environment = config('app.env', 'local'); + + $project->load(['authors', 'citations', 'tags']); + $projectAuthorPivot = $this->buildAuthorPivot($project); + $projectCitationsArray = $this->serializeCitations($project->citations); + $projectTagNames = $project->tags->pluck('name')->toArray(); + $projectSpecies = $project->species; foreach ($_studies as $study) { // $study->users()->sync($project->user()->getDictionary()); @@ -163,29 +201,217 @@ public function handle(AssignIdentifier $assigner, UpdateDOI $updater, PublishPr $dataset->save(); } + $this->copyProjectMetadataToStudy( + $study, + $projectAuthorPivot, + $projectCitationsArray, + $projectTagNames, + $projectSpecies + ); + $study->status = 'complete'; $study->save(); } } $assigner->assign($_studies); - $release_date = Carbon::parse($project->release_date); - if ($release_date->isPast()) { + $releaseDate = Carbon::parse($project->release_date); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_samples_mode_immediate_publish_all', + 'project_id' => $project->id, + 'release_date' => filled($project->release_date) + ? Carbon::parse($project->release_date)->toIso8601String() + : null, + 'studies_count' => $_studies->count(), + ]); + + if ($releaseDate->lessThanOrEqualTo(now())) { foreach ($_studies as $study) { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_samples_mode_publish_study', + 'project_id' => $project->id, + 'study_id' => $study->id, + ]); $studyPublisher->publish($study); } } $updater->update($_studies); // Notification::send($this->prepareSendList($project), new StudyPublishNotification($_studies)); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_samples_mode_before_study_publish_event', + 'project_id' => $project->id, + ]); event(new StudyPublish($_studies, $this->prepareSendList($project))); + $project->load('citations'); + foreach ($_studies as $study) { + app(SyncCitationPivot::class)->mergeProjectCitationsOntoStudy($study, $project->citations); + } + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_samples_mode_before_delete_project_draft', + 'project_id' => $project->id, + ]); $project->delete(); $draft->delete(); + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_samples_mode_complete', + 'project_id' => $project->id, + ]); + } } } - public function moveFolder($fsObject, $draft, $path) + /** + * After files are on canonical storage (or on a republish with no draft), + * resolve embargo vs published from {@see Project::$release_date}, then assign + * identifiers, optionally call {@see PublishProject::publish}, update DOIs, + * link provisional DOI, rebuild archives, and notify. + */ + private function finalizeProjectModeFromReleaseDate( + Project $project, + PublishProject $projectPublisher, + AssignIdentifier $assigner, + UpdateDOI $updater, + ): void { + $release_date = Carbon::parse($project->release_date); + if ($release_date->isFuture()) { + $project->status = 'embargo'; + } else { + $project->status = 'published'; + } + + $project->save(); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_project_mode_release_resolved', + 'project_id' => $project->id, + 'release_date' => $release_date->toIso8601String(), + 'release_is_future' => $release_date->isFuture(), + 'release_is_past' => $release_date->isPast(), + 'resolved_status' => $project->status, + ]); + + $assigner->assign($project->fresh()); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_project_mode_after_assign', + 'project_id' => $project->id, + ]); + + if ($release_date->isPast()) { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_project_mode_immediate_publish', + 'project_id' => $project->id, + ]); + $projectPublisher->publish($project); + } else { + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_project_mode_skip_publish_embargo', + 'project_id' => $project->id, + ]); + } + $updater->update($project->fresh()); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_project_mode_after_update_doi', + 'project_id' => $project->id, + ]); + + $this->linkProvisionalDoiSafely($project->fresh()); + + $this->dispatchArchives($project->fresh()); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_project_mode_before_publish_notification', + 'project_id' => $project->id, + ]); + + $project->sendNotification('publish', $this->prepareSendList($project)); + + Log::info('embargo_publish_trace', [ + 'stage' => 'process_submission_project_mode_complete', + 'project_id' => $project->id, + ]); + } + + /** + * Queue the per-project and per-study archive (ZIP) regeneration jobs + * that produce the public `…/local/archive/{uuid}/{name}.zip` downloads. + * + * Dispatching here — i.e. AFTER `moveFolder` has rewritten every + * `fsObject->path` and physically moved the S3 objects from the draft + * prefix to `local/{project.uuid}/...` — is what guarantees the archive + * jobs see the canonical post-publish layout. Dispatching earlier (e.g. + * during draft finalization) used to race against the publish-time move + * and frequently produced empty zips with no `download_url`. + * + * The `download_url` columns are reset before dispatch because both + * Archive* jobs short-circuit when a URL is already present (assuming + * a previous archive is still valid). Any zip built before the move + * is no longer valid — its contents reference the old draft prefix — + * so we force a clean rebuild. + */ + /** + * Register the project's provisional DOI on DataCite (if any) and + * bidirectionally link it to the canonical DOI via IsIdenticalTo. + * + * Failures are logged but never raised — the provisional-DOI link is a + * RDM convenience that must not block a successful publish. Independent + * samples (the Collection-of-Studies branch in `handle()`) never reach + * this path because they're published without a parent Project. + */ + private function linkProvisionalDoiSafely(Project $project): void + { + if (empty($project->provisional_doi) || empty($project->doi)) { + return; + } + + try { + $project->linkProvisionalDoi(app(DOIService::class)); + } catch (\Throwable $e) { + Log::warning('ProcessSubmission: linkProvisionalDoi failed; canonical DOI is still valid', [ + 'project_id' => $project->id, + 'doi' => $project->doi, + 'provisional_doi' => $project->provisional_doi, + 'error' => $e->getMessage(), + ]); + } + } + + private function dispatchArchives(Project $project): void + { + Log::info('embargo_publish_trace', [ + 'stage' => 'dispatch_archives', + 'project_id' => $project->id, + 'project_status' => $project->status, + ]); + + $project->forceFill(['download_url' => null])->save(); + + $project->studies()->update(['download_url' => null]); + + ArchiveProject::dispatch($project->fresh()); + ArchiveStudy::dispatch($project->fresh()); + } + + /** + * Move draft-prefixed storage into canonical publish paths. + * + * Wrapped in {@see FileSystemObject::withoutEvents()} so path rewrites do not + * run `FileSystemObjectObserver` invalidation (which would reset study archives, + * NMRium rows, and the has_nmrium flag). Publish flows enqueue a single + * post-move archive rebuild after relocation completes. + */ + public function moveFolder($fsObject, $draft, $path): void + { + FileSystemObject::withoutEvents(function () use ($fsObject, $draft, $path): void { + $this->relocateFolderTreeDuringPublish($fsObject, $draft, $path); + }); + } + + private function relocateFolderTreeDuringPublish($fsObject, $draft, $path): void { $newPath = str_replace($draft->path, $path, $fsObject->path); $fsObject->path = $newPath; @@ -199,15 +425,75 @@ public function moveFolder($fsObject, $draft, $path) $path, $fsObjectChild->path ); - Storage::disk(env('FILESYSTEM_DRIVER'))->move($fsObjectChild->path, $newPath); + Storage::disk(config('filesystems.default'))->move($fsObjectChild->path, $newPath); $fsObjectChild->path = $newPath; $fsObjectChild->save(); } else { - $this->moveFolder($fsObjectChild, $draft, $path); + $this->relocateFolderTreeDuringPublish($fsObjectChild, $draft, $path); } } } + /** + * Build the author => pivot map used to sync project authors onto a study. + * + * @return array + */ + private function buildAuthorPivot($project): array + { + $pivot = []; + foreach ($project->authors as $author) { + $pivot[$author->id] = [ + 'contributor_type' => $author->pivot->contributor_type ?? null, + 'sort_order' => $author->pivot->sort_order ?? null, + ]; + } + + return $pivot; + } + + /** + * Convert project Citation models into the JSON-array shape Study::$casts expects. + * + * @return array>|null + */ + private function serializeCitations($citations): ?array + { + if (! $citations || $citations->isEmpty()) { + return null; + } + + return $citations->map(fn ($citation) => $citation->toArray())->values()->all(); + } + + /** + * Copy project-level metadata onto a study when publishing as samples. + */ + private function copyProjectMetadataToStudy( + $study, + array $authorPivot, + ?array $citationsArray, + array $tagNames, + $species + ): void { + if (! empty($authorPivot)) { + $study->studyAuthors()->syncWithoutDetaching($authorPivot); + } + + if (! empty($citationsArray)) { + $existing = $study->citations ?? []; + $study->citations = array_merge($existing, $citationsArray); + } + + if (! empty($tagNames)) { + $study->syncTagsWithType($tagNames, 'Study'); + } + + if (! empty($species) && empty($study->species)) { + $study->species = $species; + } + } + /** * Prepare Sent to list. * @@ -216,15 +502,19 @@ public function moveFolder($fsObject, $draft, $path) */ public function prepareSendList($project) { - $sendTo = []; - foreach ($project->allUsers() as $member) { - if ($member->projectMembership->role == 'creator' || $member->projectMembership->role == 'owner') { - array_push($sendTo, $member); - } else { - array_push($sendTo, $project->owner); + $sendTo = collect(); + + if ($project->owner) { + $sendTo->push($project->owner); + } + + foreach ($project->users as $member) { + $role = $member->projectMembership?->role; + if ($role === 'creator' || $role === 'owner') { + $sendTo->push($member); } } - return $sendTo; + return $sendTo->unique('id')->values()->all(); } } diff --git a/app/Jobs/ValidateAndSubmitELNDraft.php b/app/Jobs/ValidateAndSubmitELNDraft.php index d5fbb7376..a15c2c5ed 100644 --- a/app/Jobs/ValidateAndSubmitELNDraft.php +++ b/app/Jobs/ValidateAndSubmitELNDraft.php @@ -26,10 +26,21 @@ public function handle(): void if (! $draft) { Log::error("Draft not found for finalizer: {$this->draftId}"); + Log::info('embargo_publish_trace', [ + 'stage' => 'validate_and_submit_eln_draft_aborted', + 'reason' => 'draft_not_found', + 'draft_id' => $this->draftId, + ]); + return; } try { + Log::info('embargo_publish_trace', [ + 'stage' => 'validate_and_submit_eln_draft_start', + 'draft_id' => $draft->id, + ]); + Log::info('Starting final processing after ProcessELNSpectra', [ 'draft_id' => $draft->id, ]); @@ -38,6 +49,12 @@ public function handle(): void if (! $project) { Log::error('No project found for draft in finalizer', ['draft_id' => $draft->id]); + Log::info('embargo_publish_trace', [ + 'stage' => 'validate_and_submit_eln_draft_aborted', + 'reason' => 'project_not_found', + 'draft_id' => $draft->id, + ]); + return; } @@ -78,6 +95,12 @@ public function handle(): void // Dispatch ProcessSubmission if validation passes if ($status) { + Log::info('embargo_publish_trace', [ + 'stage' => 'validate_and_submit_eln_draft_validation_passed', + 'project_id' => $project->id, + 'draft_id' => $draft->id, + ]); + Log::info('Validation passed for project, dispatching ProcessSubmission', [ 'project_id' => $project->id, 'draft_id' => $draft->id, @@ -87,7 +110,19 @@ public function handle(): void $this->trackSubmissionValidated($draft, $project, $project->owner); ProcessSubmission::dispatch($project); + + Log::info('embargo_publish_trace', [ + 'stage' => 'validate_and_submit_eln_draft_process_submission_dispatched', + 'project_id' => $project->id, + 'draft_id' => $draft->id, + ]); } else { + Log::info('embargo_publish_trace', [ + 'stage' => 'validate_and_submit_eln_draft_validation_failed', + 'project_id' => $project->id, + 'draft_id' => $draft->id, + ]); + Log::error('Validation failed for project', [ 'project_id' => $project->id, 'draft_id' => $draft->id, @@ -103,6 +138,13 @@ public function handle(): void 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); + + Log::info('embargo_publish_trace', [ + 'stage' => 'validate_and_submit_eln_draft_exception', + 'draft_id' => $draft->id, + 'error' => $e->getMessage(), + ]); + throw $e; } } diff --git a/app/Jobs/VerifyFileIntegrityJob.php b/app/Jobs/VerifyFileIntegrityJob.php index 33e3369fc..191df21df 100644 --- a/app/Jobs/VerifyFileIntegrityJob.php +++ b/app/Jobs/VerifyFileIntegrityJob.php @@ -74,34 +74,32 @@ public function handle(FileIntegrityService $integrityService): void // Handle files without existing checksums (calculate initial checksums) if (! $this->fileSystemObject->getPrimaryChecksum()) { - Log::info('No existing checksum found, calculating initial checksums', [ + Log::info('No existing checksum found, skipping verification', [ 'file_id' => $this->fileSystemObject->id, 'file_name' => $this->fileSystemObject->name, ]); try { - // Calculate and store initial checksums - $fileIntegrityService = app(FileIntegrityService::class); - $result = $fileIntegrityService->verifyFileIntegrity($this->fileSystemObject, true); + // Attempt to verify file integrity (will mark as failed if no checksum exists) + $verificationSuccess = $integrityService->verifyFileIntegrity($this->fileSystemObject); - if ($result['success']) { - Log::info('Initial checksums calculated and stored successfully', [ + if ($verificationSuccess) { + Log::info('File integrity verification completed successfully', [ 'file_id' => $this->fileSystemObject->id, 'file_name' => $this->fileSystemObject->name, - 'checksums' => $result['checksums'] ?? null, ]); } else { - Log::error('Failed to calculate initial checksums', [ + Log::warning('File integrity verification failed - no checksum available', [ 'file_id' => $this->fileSystemObject->id, 'file_name' => $this->fileSystemObject->name, - 'error' => $result['error'] ?? 'Unknown error', + 'error' => $this->fileSystemObject->integrity_error, ]); } return; } catch (\Exception $e) { - Log::error('Exception while calculating initial checksums', [ + Log::error('Exception while verifying file without checksum', [ 'file_id' => $this->fileSystemObject->id, 'file_name' => $this->fileSystemObject->name, 'error' => $e->getMessage(), diff --git a/app/Listeners/ProjectArchival.php b/app/Listeners/ProjectArchival.php index a1d7580d5..9693f2b53 100644 --- a/app/Listeners/ProjectArchival.php +++ b/app/Listeners/ProjectArchival.php @@ -2,9 +2,11 @@ namespace App\Listeners; +use App\Events\ProjectArchival as ProjectArchivalEvent; use App\Models\User; use App\Notifications\ProjectArchivalNotification; use App\Notifications\ProjectArchivalNotificationToAdmins; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; class ProjectArchival @@ -20,8 +22,14 @@ public function __construct() /** * Handle the event. */ - public function handle(object $event): void + public function handle(ProjectArchivalEvent $event): void { + Log::info('embargo_publish_trace', [ + 'stage' => 'project_archival_listener_handle', + 'project_id' => $event->project->id, + 'recipient_count' => is_countable($event->sendTo) ? count($event->sendTo) : 0, + ]); + Notification::send($event->sendTo, new ProjectArchivalNotification($event->project)); Notification::send(User::role(['super-admin'])->get(), new ProjectArchivalNotificationToAdmins($event->project)); } diff --git a/app/Listeners/ProjectDeletion.php b/app/Listeners/ProjectDeletion.php index 576b9f515..e46643c8d 100644 --- a/app/Listeners/ProjectDeletion.php +++ b/app/Listeners/ProjectDeletion.php @@ -2,7 +2,9 @@ namespace App\Listeners; +use App\Events\ProjectDeletion as ProjectDeletionEvent; use App\Notifications\ProjectDeletionNotification; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; class ProjectDeletion @@ -15,8 +17,14 @@ public function __construct() {} /** * Handle the event. */ - public function handle(object $event): void + public function handle(ProjectDeletionEvent $event): void { + Log::info('embargo_publish_trace', [ + 'stage' => 'project_deletion_listener_handle', + 'project_id' => $event->project->id, + 'recipient_count' => is_countable($event->sendTo) ? count($event->sendTo) : 0, + ]); + Notification::send($event->sendTo, new ProjectDeletionNotification($event->project)); } } diff --git a/app/Listeners/SendDraftProcessedNotification.php b/app/Listeners/SendDraftProcessedNotification.php index 1042a7172..c0f496c41 100644 --- a/app/Listeners/SendDraftProcessedNotification.php +++ b/app/Listeners/SendDraftProcessedNotification.php @@ -2,9 +2,11 @@ namespace App\Listeners; +use App\Events\DraftProcessed as DraftProcessedEvent; use App\Models\User; use App\Notifications\DraftProcessedNotification; use App\Notifications\DraftProcessedNotificationToAdmin; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; class SendDraftProcessedNotification @@ -22,8 +24,14 @@ public function __construct() /** * Handle the event. */ - public function handle(object $event): void + public function handle(DraftProcessedEvent $event): void { + Log::info('embargo_publish_trace', [ + 'stage' => 'draft_processed_listener_handle', + 'project_id' => $event->project->id, + 'recipient_count' => is_countable($event->sendTo) ? count($event->sendTo) : 0, + ]); + Notification::send($event->sendTo, new DraftProcessedNotification($event->project)); Notification::send(User::role(['super-admin'])->get(), new DraftProcessedNotificationToAdmin($event->project, null)); } diff --git a/app/Listeners/StudyPublish.php b/app/Listeners/StudyPublish.php index e58023762..d01a2ab46 100644 --- a/app/Listeners/StudyPublish.php +++ b/app/Listeners/StudyPublish.php @@ -2,9 +2,11 @@ namespace App\Listeners; +use App\Events\StudyPublish as StudyPublishEvent; use App\Models\User; use App\Notifications\DraftProcessedNotificationToAdmin; use App\Notifications\StudyPublishNotification; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; class StudyPublish @@ -24,8 +26,14 @@ public function __construct() * * @param object $event */ - public function handle($event): void + public function handle(StudyPublishEvent $event): void { + Log::info('embargo_publish_trace', [ + 'stage' => 'study_publish_listener_handle', + 'study_ids' => collect($event->studies)->pluck('id')->values()->all(), + 'recipient_count' => is_countable($event->sendTo) ? count($event->sendTo) : 0, + ]); + Notification::send($event->sendTo, new StudyPublishNotification($event->studies)); Notification::send(User::role(['super-admin'])->get(), new DraftProcessedNotificationToAdmin(null, $event->studies)); } diff --git a/app/Mail/DraftProcessed.php b/app/Mail/DraftProcessed.php index 54201fd6e..f29db6cf3 100644 --- a/app/Mail/DraftProcessed.php +++ b/app/Mail/DraftProcessed.php @@ -13,45 +13,44 @@ class DraftProcessed extends Mailable public $project; - /** - * Create a new message instance. - * - * @return void - */ public function __construct($project) { $this->project = $project; } - /** - * Build the message. - * - * @return $this - */ public function build() { - $releasedToday = false; - $releaseDate = Carbon::parse($this->project->release_date); + $project = $this->project; - if ($releaseDate->isToday()) { - $releasedToday = true; - } + /** + * This mail is sent after processing completes. + * Whether it's an embargo vs immediate public release should be based on + * the project's actual published state, not a timezone-sensitive date + * comparison. + */ + $releasedToday = (bool) ($project->is_public ?? false); + + $releaseDate = filled($project->release_date) + ? Carbon::parse($project->release_date) + : null; // Safely handle identifier parsing $publicUrlPath = ''; - if ($this->project->identifier && str_contains($this->project->identifier, ':')) { - $identifierParts = explode(':', $this->project->identifier); + if ($project->identifier && str_contains($project->identifier, ':')) { + $identifierParts = explode(':', $project->identifier); if (count($identifierParts) > 1) { $publicUrlPath = $identifierParts[1]; } } return $this->markdown('vendor.mail.draft-processed', [ - 'url' => url(config('app.url').'/dashboard/projects/'.$this->project->id), - 'project' => $this->project, + 'url' => url(config('app.url').'/dashboard/projects/'.$project->id), + 'project' => $project, 'releasedToday' => $releasedToday, - 'releaseDate' => explode(' ', $releaseDate)[0], - 'publicUrl' => $publicUrlPath ? url(config('app.url').'/'.$publicUrlPath) : url(config('app.url').'/dashboard/projects/'.$this->project->id), - ])->subject(__('Submission Processed'.' - '.$this->project->name)); + 'releaseDate' => $releaseDate?->toDateString(), + 'publicUrl' => $publicUrlPath + ? url(config('app.url').'/'.$publicUrlPath) + : url(config('app.url').'/dashboard/projects/'.$project->id), + ])->subject(__('Submission Processed'.' - '.$project->name)); } } diff --git a/app/Mail/DraftProcessedNotifyAdmins.php b/app/Mail/DraftProcessedNotifyAdmins.php index 1dadab2f6..f094a5a4f 100644 --- a/app/Mail/DraftProcessedNotifyAdmins.php +++ b/app/Mail/DraftProcessedNotifyAdmins.php @@ -15,43 +15,36 @@ class DraftProcessedNotifyAdmins extends Mailable public $studies; - /** - * Create a new message instance. - * - * @return void - */ public function __construct($project, $studies) { $this->project = $project; $this->studies = $studies; } - /** - * Build the message. - * - * @return $this - */ public function build() { $releasedToday = false; - $releaseDate = Carbon::parse($this->project->release_date); - - if ($releaseDate->isToday()) { - $releasedToday = true; - } if ($this->project) { + $releasedToday = (bool) ($this->project->is_public ?? false); + + $releaseDate = filled($this->project->release_date) + ? Carbon::parse($this->project->release_date)->toDateString() + : null; + return $this->markdown('vendor.mail.project-published-notify-admins', [ 'url' => url(config('app.url').'/dashboard/projects/'.$this->project->id), 'projectName' => $this->project->name, 'projectId' => $this->project->id, 'releasedToday' => $releasedToday, + 'releaseDate' => $releaseDate, ])->subject(__('A project has been published'.' - '.$this->project->name)); } elseif ($this->studies) { + $studies = $this->studies; + return $this->markdown('vendor.mail.study-published-notify-admins', [ - 'url' => url(config('app.url').'/spectra'), - 'samples' => $this->studies, - 'releasedToday' => $releasedToday, + 'url' => url(config('app.url').'/projects'), + 'samples' => $studies, ])->subject(__('Sample(s) has been published.')); } diff --git a/app/Mail/EmbargoPublicationFailed.php b/app/Mail/EmbargoPublicationFailed.php new file mode 100644 index 000000000..387b5d869 --- /dev/null +++ b/app/Mail/EmbargoPublicationFailed.php @@ -0,0 +1,140 @@ +markdown('vendor.mail.embargo-publication-failed', [ + 'url' => url(config('app.url').'/dashboard/projects/'.$this->project->id), + 'project' => $this->project, + 'reason' => $this->reason, + 'validationFailures' => $this->validationFailures(), + 'exceptionClass' => $this->exceptionClass, + 'admin' => $this->admin, + ])->subject(__('Embargo publication failed - '.$this->project->name)); + } + + private function validationFailures(): array + { + $report = $this->validation?->report; + + if (! is_array($report)) { + return []; + } + + $failures = []; + $project = $report['project'] ?? []; + + $labels = [ + 'title' => 'Project name', + 'description' => 'Project description', + 'keywords' => 'Project keywords', + 'citations' => 'Project citations', + 'authors' => 'Project authors', + 'license' => 'Project license', + 'image' => 'Project profile image', + ]; + + $hasCitationDetailFailures = $this->hasCitationDetailFailures($project); + + foreach ($labels as $field => $label) { + if ($field === 'citations' && $hasCitationDetailFailures) { + continue; + } + + $this->addFailure($failures, $label, $project[$field] ?? null); + } + + foreach (($project['citations_detail'] ?? []) as $citationIndex => $citation) { + $citationLabel = $citation['name'] ?? 'Citation '.($citationIndex + 1); + + $this->addFailure($failures, "{$citationLabel}: DOI", $citation['doi'] ?? null); + } + + foreach (($project['studies'] ?? []) as $studyIndex => $study) { + $studyLabel = $study['name'] ?? 'Sample '.($studyIndex + 1); + + $studyLabels = [ + 'title' => 'sample title', + 'description' => 'sample description', + 'keywords' => 'sample keywords', + 'sample' => 'sample metadata', + 'nmrium_info' => 'spectra', + 'molecules' => 'compound information', + ]; + + foreach ($studyLabels as $field => $label) { + $this->addFailure($failures, "{$studyLabel}: {$label}", $study[$field] ?? null); + } + + foreach (($study['datasets'] ?? []) as $datasetIndex => $dataset) { + $datasetLabel = $dataset['name'] ?? "{$studyLabel}: spectral dataset ".($datasetIndex + 1); + + $datasetLabels = [ + 'files' => 'files', + 'nmrium_info' => 'spectra', + 'assay' => 'assay metadata', + 'assignments' => 'assignments', + ]; + + foreach ($datasetLabels as $field => $label) { + $this->addFailure($failures, "{$datasetLabel}: {$label}", $dataset[$field] ?? null); + } + } + } + + return $failures; + } + + private function addFailure(array &$failures, string $label, mixed $value): void + { + if (is_string($value) && str_starts_with($value, 'false|')) { + $rule = substr($value, 6); + + if ($this->isRequiredRule($rule)) { + $failures[] = $label.' ('.$this->formatRule($rule).')'; + } + } + } + + private function formatRule(string $rule): string + { + return str_replace('|', ', ', $rule); + } + + private function isRequiredRule(string $rule): bool + { + return in_array('required', explode('|', $rule), true); + } + + private function hasCitationDetailFailures(array $project): bool + { + foreach (($project['citations_detail'] ?? []) as $citation) { + $value = $citation['doi'] ?? null; + + if (is_string($value) && str_starts_with($value, 'false|') && $this->isRequiredRule(substr($value, 6))) { + return true; + } + } + + return false; + } +} diff --git a/app/Mail/EmbargoReleaseReminder.php b/app/Mail/EmbargoReleaseReminder.php new file mode 100644 index 000000000..8917f33b7 --- /dev/null +++ b/app/Mail/EmbargoReleaseReminder.php @@ -0,0 +1,51 @@ +project = $project; + $this->daysUntilRelease = $daysUntilRelease; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $releaseDate = Carbon::parse($this->project->release_date)->format('M d, Y'); + $subject = match ($this->daysUntilRelease) { + 7 => 'Your embargo project will be released in 1 week', + 3 => 'Your embargo project will be released in 3 days', + 1 => 'Your embargo project will be released tomorrow', + default => 'Your embargo project release is approaching' + }; + + return $this->markdown('vendor.mail.embargo-release-reminder', [ + 'url' => url(config('app.url').'/dashboard/projects/'.$this->project->id), + 'projectName' => $this->project->name, + 'releaseDate' => $releaseDate, + 'daysUntilRelease' => $this->daysUntilRelease, + ])->subject($subject.' - '.$this->project->name); + } +} diff --git a/app/Mail/ProjectDeletion.php b/app/Mail/ProjectDeletion.php index 8d1547b74..2b02e53f8 100644 --- a/app/Mail/ProjectDeletion.php +++ b/app/Mail/ProjectDeletion.php @@ -30,10 +30,10 @@ public function __construct($project) */ public function build() { - $coolOffPeriod = (int) env('COOL_OFF_PERIOD', '30'); + $coolOffPeriod = config('nmrxiv.cool_off_period'); return $this->markdown('vendor.mail.project-deletion', [ - 'url' => url(config('app.url').'/dashboard/projects/'.$this->project->id.'/settings'), + 'url' => url('/dashboard/projects/'.$this->project->id.'/settings'), 'projectName' => $this->project->name, 'deletedOn' => explode(' ', $this->project->deleted_on)[0], 'dueDate' => explode(' ', Carbon::parse($this->project->deleted_on)->addDays($coolOffPeriod))[0], diff --git a/app/Mail/ProjectDeletionReminder.php b/app/Mail/ProjectDeletionReminder.php index b726b406f..30c406e51 100644 --- a/app/Mail/ProjectDeletionReminder.php +++ b/app/Mail/ProjectDeletionReminder.php @@ -30,10 +30,10 @@ public function __construct($project) */ public function build() { - $coolOffPeriod = (int) env('COOL_OFF_PERIOD', '30'); + $coolOffPeriod = config('nmrxiv.cool_off_period'); return $this->markdown('vendor.mail.project-deletion-reminder', [ - 'url' => url(config('app.url').'/dashboard/projects/'.$this->project->id), + 'url' => url('/dashboard/projects/'.$this->project->id), 'projectName' => $this->project->name, 'deletedOn' => explode(' ', $this->project->deleted_on)[0], 'dueDate' => explode(' ', Carbon::parse($this->project->deleted_on)->addDays($coolOffPeriod))[0], diff --git a/app/Mail/ProjectInvitation.php b/app/Mail/ProjectInvitation.php index 409c92759..bba952be4 100644 --- a/app/Mail/ProjectInvitation.php +++ b/app/Mail/ProjectInvitation.php @@ -16,7 +16,7 @@ class ProjectInvitation extends Mailable implements ShouldQueue /** * The project invitation instance. * - * @var \App\Models\ProjectInvitation + * @var ProjectInvitationModel */ public $invitation; diff --git a/app/Mail/StudyInvitation.php b/app/Mail/StudyInvitation.php index 12b10dbde..41e91f22a 100644 --- a/app/Mail/StudyInvitation.php +++ b/app/Mail/StudyInvitation.php @@ -16,7 +16,7 @@ class StudyInvitation extends Mailable implements ShouldQueue /** * The study invitation instance. * - * @var \App\Models\StudyInvitation + * @var StudyInvitationModel */ public $invitation; diff --git a/app/Mail/StudyPublish.php b/app/Mail/StudyPublish.php index caa270da7..d93abda66 100644 --- a/app/Mail/StudyPublish.php +++ b/app/Mail/StudyPublish.php @@ -5,7 +5,6 @@ use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Carbon; class StudyPublish extends Mailable { @@ -13,35 +12,18 @@ class StudyPublish extends Mailable public $studies; - /** - * Create a new message instance. - * - * @return void - */ public function __construct($studies) { $this->studies = $studies; } - /** - * Build the message. - * - * @return $this - */ public function build() { - $releaseToday = false; - $releaseDate = Carbon::parse($this->studies[0]['release_date']); - - if ($releaseDate->isToday()) { - $releaseToday = true; - } + $studies = $this->studies; return $this->markdown('vendor.mail.study-published', [ - 'url' => url(config('app.url').'/spectra'), - 'samples' => $this->studies, - 'releaseToday' => $releaseToday, - 'releaseDate' => $releaseDate, + 'url' => url(config('app.url').'/projects'), + 'samples' => $studies, ])->subject(__('Submission Processed.')); } } diff --git a/app/Mail/TeamInvitation.php b/app/Mail/TeamInvitation.php index e1488a363..dbbf63798 100644 --- a/app/Mail/TeamInvitation.php +++ b/app/Mail/TeamInvitation.php @@ -16,7 +16,7 @@ class TeamInvitation extends Mailable implements ShouldQueue /** * The study invitation instance. * - * @var \App\Models\TeamInvitation + * @var TeamInvitationModel */ public $invitation; diff --git a/app/Models/Announcement.php b/app/Models/Announcement.php index f2c26b3a0..a215bab3d 100644 --- a/app/Models/Announcement.php +++ b/app/Models/Announcement.php @@ -35,9 +35,11 @@ public function owner(): BelongsTo */ public static function active() { - return (new static)::where('status', 'active')->where(function ($q) { - $q->where('start_time', '<=', Carbon::now()); - $q->where('end_time', '>=', Carbon::now()); + $now = Carbon::now(); + + return (new static)::where('status', 'active')->where(function ($q) use ($now) { + $q->where('start_time', '<=', $now); + $q->where('end_time', '>=', $now); })->get(); } } diff --git a/app/Models/Author.php b/app/Models/Author.php index 111409ed0..16352f267 100644 --- a/app/Models/Author.php +++ b/app/Models/Author.php @@ -13,7 +13,7 @@ class Author extends Model /** * Projects that belongs to authors. * - * @return \Illuminate\Database\Eloquent\Relations\belongsToMany + * @return BelongsToMany */ protected $fillable = [ 'title', @@ -22,10 +22,16 @@ class Author extends Model 'family_name', 'email_id', 'affiliation', + 'ror_id', ]; public function projects(): BelongsToMany { return $this->belongsToMany(Project::class); } + + public function studies(): BelongsToMany + { + return $this->belongsToMany(Study::class, 'author_study'); + } } diff --git a/app/Models/Citation.php b/app/Models/Citation.php index efa96c31e..76cec68b1 100644 --- a/app/Models/Citation.php +++ b/app/Models/Citation.php @@ -13,6 +13,7 @@ class Citation extends Model protected $fillable = [ 'doi', 'title', + 'title_slug', 'authors', 'citation_text', ]; @@ -35,4 +36,12 @@ public function projects(): BelongsToMany { return $this->belongsToMany(Project::class); } + + /** + * Studies linked to this citation (normalized pivot; distinct from JSON `studies.citations`). + */ + public function studies(): BelongsToMany + { + return $this->belongsToMany(Study::class); + } } diff --git a/app/Models/Dataset.php b/app/Models/Dataset.php index 30b19d9f3..5861d848e 100644 --- a/app/Models/Dataset.php +++ b/app/Models/Dataset.php @@ -6,7 +6,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\MorphOne; use OwenIt\Auditing\Contracts\Auditable; use Storage; @@ -39,6 +38,7 @@ class Dataset extends Model implements Auditable 'dataset_photo_path', 'license_id', 'external_url', + 'assignments', ]; /** @@ -52,6 +52,40 @@ class Dataset extends Model implements Auditable 'dataset_photo_url', ]; + /** + * The attributes that should be cast. + */ + protected function casts(): array + { + return [ + 'starred' => 'boolean', + 'is_public' => 'boolean', + 'assignments' => 'array', + ]; + } + + /** + * True when the dataset has user-supplied assignment content. We treat + * any non-empty `acs` string OR any `atom_peaks` rows as "assigned", + * which keeps the validator (`assignments|array|min:1`) and UI badges + * in sync with what the user actually saved on the Assignments tab. + */ + public function hasAssignments(): bool + { + $a = $this->assignments; + if (! is_array($a)) { + return false; + } + if (! empty($a['acs']) && trim((string) $a['acs']) !== '') { + return true; + } + if (! empty($a['atom_peaks']) && is_array($a['atom_peaks']) && count($a['atom_peaks']) > 0) { + return true; + } + + return false; + } + /** * Get the dataset identifier */ @@ -70,19 +104,19 @@ protected function identifier(): Attribute public function getDatasetPhotoUrlAttribute() { return $this->dataset_photo_path - ? Storage::disk(env('FILESYSTEM_DRIVER_PUBLIC'))->url($this->dataset_photo_path) + ? Storage::disk(config('filesystems.default_public'))->url($this->dataset_photo_path) : ''; } protected function getPublicUrlAttribute() { // return env('APP_URL', null).'/datasets/'.urlencode($this->slug); - return env('APP_URL', null).'/dataset/D'.$this->getRawOriginal('identifier'); + return config('app.url').'/dataset/D'.$this->getRawOriginal('identifier'); } protected function getPrivateUrlAttribute() { - return env('APP_URL', null).'/datasets/'.urlencode($this->url); + return config('app.url').'/datasets/'.urlencode($this->url); } public function study(): BelongsTo @@ -120,9 +154,57 @@ public function nmrium(): MorphOne return $this->morphOne(NMRium::class, 'nmriumable'); } - public function fsObject(): HasOne + /** + * NMRium workspace payload for this dataset, normalized for API and UI. + * Molecule headers are merged from the study sample when present. + * + * @return array|null + */ + public function normalizedNmriumInfo(): ?array + { + $nmrium = $this->nmrium; + if (! $nmrium) { + return null; + } + + $nmriumInfo = $nmrium->nmrium_info; + if (is_string($nmriumInfo)) { + $nmriumInfo = json_decode($nmriumInfo, true); + } + if (! is_array($nmriumInfo)) { + $nmriumInfo = []; + } + if (! isset($nmriumInfo['data']) || ! is_array($nmriumInfo['data'])) { + $nmriumInfo['data'] = []; + } + if (! isset($nmriumInfo['data']['molecules']) || ! is_array($nmriumInfo['data']['molecules'])) { + $nmriumInfo['data']['molecules'] = []; + } + + $sample = optional($this->study)->sample; + if ($sample) { + $nmriumInfo['data']['molecules'] = $sample + ->mergeNmriumMolecules($nmriumInfo['data']['molecules']); + } + + return $nmriumInfo; + } + + /** + * The directory/file backing this dataset. + * + * The canonical link is `datasets.fs_id -> file_system_objects.id`, + * which is set atomically when the dataset is created from a folder + * (see `App\Actions\Draft\ProcessDraft::createDatasetFromOrphanedFile` + * and the chemotion path nearby). The reverse `file_system_objects.dataset_id` + * back-pointer is only updated by a separate `save()` and gets out of + * sync after re-archive, partial reseed, or any flow that recreates the + * dataset row without re-walking the fs tree. Defining the relationship + * as `belongsTo` on `fs_id` keeps it correct in those cases too. + */ + public function fsObject(): BelongsTo { - return $this->hasOne(FileSystemObject::class); + return $this->belongsTo(FileSystemObject::class, 'fs_id'); } /** diff --git a/app/Models/EmbargoReminder.php b/app/Models/EmbargoReminder.php new file mode 100644 index 000000000..1944ccde5 --- /dev/null +++ b/app/Models/EmbargoReminder.php @@ -0,0 +1,38 @@ + + */ + protected function casts(): array + { + return [ + 'sent_at' => 'datetime', + ]; + } + + /** + * Get the project that owns the embargo reminder. + */ + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } +} diff --git a/app/Models/FileSystemObject.php b/app/Models/FileSystemObject.php index 1c21a578a..1165d1e7d 100644 --- a/app/Models/FileSystemObject.php +++ b/app/Models/FileSystemObject.php @@ -2,11 +2,14 @@ namespace App\Models; +use App\Observers\FileSystemObjectObserver; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +#[ObservedBy([FileSystemObjectObserver::class])] class FileSystemObject extends Model { use HasFactory; diff --git a/app/Models/HasDOI.php b/app/Models/HasDOI.php index 8ea41df87..0a98fafcb 100644 --- a/app/Models/HasDOI.php +++ b/app/Models/HasDOI.php @@ -2,11 +2,15 @@ namespace App\Models; +use App\Support\DataCite\MetadataEnricher; +use Carbon\Carbon; +use Illuminate\Support\Facades\Log; + trait HasDOI { public function generateDOI($doiService) { - $doi_host = env('DOI_HOST', null); + $doi_host = config('doi.host'); if (! is_null($doi_host)) { $identifier = $this->getIdentifier($this, 'identifier'); @@ -71,7 +75,7 @@ public function generateDOI($doiService) */ public function updateDOIMetadata($doiService) { - $doi_host = env('DOI_HOST', null); + $doi_host = config('doi.host'); if (! is_null($doi_host)) { $doi = $this->doi; @@ -96,24 +100,32 @@ public function getMetadata() $users = []; $keywords = []; $citations = []; + + $issuedDate = $this->release_date ?? $this->updated_at ?? Carbon::now(); + $dates = [ [ - 'date' => $this->release_date, + 'date' => (string) $issuedDate, + 'dateType' => 'Issued', + ], + [ + 'date' => (string) ($this->release_date ?? $issuedDate), 'dateType' => 'Available', ], [ - 'date' => $this->created_at, + 'date' => (string) ($this->created_at ?? $issuedDate), 'dateType' => 'Submitted', ], [ - 'date' => $this->updated_at, + 'date' => (string) ($this->updated_at ?? $issuedDate), 'dateType' => 'Updated', ], ]; $description = [ 'description' => $this->description, - 'descriptionType' => 'Other', + 'descriptionType' => 'Abstract', + 'lang' => 'en', ]; if ($this instanceof Project) { @@ -154,44 +166,75 @@ public function getMetadata() 'nameType' => 'Personal', 'givenName' => $author->given_name, 'familyName' => $author->family_name, - 'nameIdentifiers' => [ + ]; + + if (! empty($author->orcid_id)) { + $creator['nameIdentifiers'] = [ [ - 'nameIdentifier' => $author->orcid_id ? $author->orcid_id : null, + 'nameIdentifier' => $author->orcid_id, 'nameIdentifierScheme' => 'ORCID', 'schemeUri' => 'https://orcid.org', ], - ], - 'affiliation' => [$author->affiliation ? $author->affiliation : null], - ]; + ]; + } + + $authorAffiliations = array_values(array_filter([$author->affiliation ? $author->affiliation : null])); + if ($authorAffiliations !== []) { + $creator['affiliation'] = $authorAffiliations; + } + array_push($creators, $creator); } + $ownerId = $this->owner_id ?? null; + $contributors = []; foreach ($users as $user) { + $contributorType = ($ownerId !== null && (int) $user->id === (int) $ownerId) + ? 'ContactPerson' + : 'Researcher'; + $contributor = [ - 'contributorType' => 'Other', + 'contributorType' => $contributorType, 'name' => $user->last_name.', '.$user->first_name, 'nameType' => 'Personal', 'givenName' => $user->first_name, 'familyName' => $user->last_name, - 'nameIdentifiers' => [ + ]; + + if (! empty($user->orcid_id)) { + $contributor['nameIdentifiers'] = [ [ - 'nameIdentifier' => $user->orcid_id ? $user->orcid_id : null, + 'nameIdentifier' => $user->orcid_id, 'nameIdentifierScheme' => 'ORCID', 'schemeUri' => 'https://orcid.org', ], - ], - 'affiliation' => [$user->affiliation ? $user->affiliation : null], - ]; + ]; + } + + $userAffiliations = array_values(array_filter([$user->affiliation ? $user->affiliation : null])); + if ($userAffiliations !== []) { + $contributor['affiliation'] = $userAffiliations; + } + array_push($contributors, $contributor); } + $hostingInstitution = $this->buildHostingInstitutionContributor(); + if ($hostingInstitution !== null) { + array_push($contributors, $hostingInstitution); + } + $relatedIdentifiers = []; foreach ($citations as $citation) { + $citationDoi = is_object($citation) ? ($citation->doi ?? null) : ($citation['doi'] ?? null); + if (empty($citationDoi)) { + continue; + } $relatedIdentifier = [ - 'relatedIdentifier' => $citation->doi ? $citation->doi : null, + 'relatedIdentifier' => $citationDoi, 'relatedIdentifierType' => 'DOI', - 'relationType' => 'IsSupplementTo', + 'relationType' => 'IsReferencedBy', ]; array_push($relatedIdentifiers, $relatedIdentifier); } @@ -221,28 +264,56 @@ public function getMetadata() array_push($subjects, $subject); } + $hasMetadataLinks = $this->buildHasMetadataLinks(); + foreach ($hasMetadataLinks as $link) { + array_push($relatedIdentifiers, $link); + } + + $isCompiledBy = $this->buildIsCompiledByLinks(); + foreach ($isCompiledBy as $entry) { + array_push($relatedIdentifiers, $entry); + } + $attributes = [ 'creators' => count($creators) > 0 ? $creators : $contributors, 'titles' => [ [ 'title' => $title, + 'lang' => 'en', ], ], + 'publisher' => $this->buildPublisher(), + 'publicationYear' => (int) Carbon::parse($issuedDate)->format('Y'), 'subjects' => $subjects, 'contributors' => $contributors, 'dates' => $dates, 'language' => 'en', 'rightsList' => $rights, 'descriptions' => [$description], + 'alternateIdentifiers' => [], + 'sizes' => [], + 'formats' => [], 'relatedIdentifiers' => $relatedIdentifiers, 'isActive' => true, 'event' => 'publish', 'state' => 'findable', - 'schemaVersion' => 'http://datacite.org/schema/kernel-4', - + 'schemaVersion' => 'http://datacite.org/schema/kernel-4.4', ]; + $attributes = $this->mergeNmrEnrichment($attributes); + + if ($this instanceof Project + && ! empty($this->provisional_doi) + && $this->provisional_doi !== $this->doi + ) { + $attributes['relatedIdentifiers'][] = [ + 'relatedIdentifier' => $this->provisional_doi, + 'relatedIdentifierType' => 'DOI', + 'relationType' => 'IsIdenticalTo', + ]; + } + return $attributes; } @@ -311,4 +382,322 @@ public function addRelatedIdentifiers($doiService) $this->save(); } } + + /** + * Register the project's provisional DOI (a placeholder string stored + * on `projects.provisional_doi`) as a real findable DataCite record + * and bidirectionally link it to the canonical DOI via IsIdenticalTo. + * + * Project-only and idempotent: any of the following short-circuits the + * call without making a DataCite request. + * + * - `$this` is not a Project (Study/Dataset never carry a provisional_doi). + * - `$this->provisional_doi` is null/empty. + * - `$this->provisional_doi` equals `$this->doi`. + * - `$this->doi` is null (canonical not yet minted). + * - `$this->provisional_doi_registered_at` is already set (unless `$force`). + * + * Independent samples (Studies published outside any Project) reach this + * method only via `instanceof Project`, so they skip naturally. + * + * @param bool $force When true, registers even if `provisional_doi_registered_at` is set. + */ + public function linkProvisionalDoi($doiService, bool $force = false): void + { + if (! ($this instanceof Project)) { + return; + } + + $provisional = $this->provisional_doi ?? null; + if (empty($provisional)) { + return; + } + if ($this->doi === null || $provisional === $this->doi) { + return; + } + if (! $force && $this->provisional_doi_registered_at !== null) { + return; + } + + $canonicalUrl = 'https://www.nmrxiv.org/project/P'.$this->getRawOriginal('identifier'); + + try { + $provisionalAttributes = $this->getMetadata(); + $provisionalAttributes['url'] = $canonicalUrl; + $provisionalAttributes['types'] = [ + 'ris' => 'DATA', + 'bibtex' => 'misc', + 'citeproc' => 'dataset', + 'schemaOrg' => 'Dataset', + 'resourceType' => 'Project', + 'resourceTypeGeneral' => 'Dataset', + ]; + + $provisionalAttributes['relatedIdentifiers'] = $this->stripIsIdenticalTo( + $provisionalAttributes['relatedIdentifiers'] ?? [] + ); + $provisionalAttributes['relatedIdentifiers'][] = [ + 'relatedIdentifier' => $this->doi, + 'relatedIdentifierType' => 'DOI', + 'relationType' => 'IsIdenticalTo', + ]; + + try { + $doiService->createCustomDOI($provisional, $provisionalAttributes); + } catch (\Throwable $e) { + Log::warning('linkProvisionalDoi: createCustomDOI failed (may already exist)', [ + 'project_id' => $this->id, + 'provisional_doi' => $provisional, + 'error' => $e->getMessage(), + ]); + } + + $existing = []; + try { + $existing = $doiService->getRelatedIdentifiers($this->doi); + } catch (\Throwable $e) { + Log::warning('linkProvisionalDoi: getRelatedIdentifiers failed; using empty baseline', [ + 'project_id' => $this->id, + 'doi' => $this->doi, + 'error' => $e->getMessage(), + ]); + } + + $merged = $this->mergeRelatedIdentifier($existing, [ + 'relatedIdentifier' => $provisional, + 'relatedIdentifierType' => 'DOI', + 'relationType' => 'IsIdenticalTo', + ]); + + $doiService->putRelatedIdentifiers($this->doi, $merged); + + $this->provisional_doi_registered_at = Carbon::now(); + $this->save(); + } catch (\Throwable $e) { + Log::error('linkProvisionalDoi failed', [ + 'project_id' => $this->id ?? null, + 'doi' => $this->doi ?? null, + 'provisional_doi' => $provisional, + 'error' => $e->getMessage(), + ]); + throw $e; + } + } + + // ------------------------------------------------------------------ // + // Internal helpers // + // ------------------------------------------------------------------ // + + /** + * Merge the MIChI v1 / sample / dataset enrichment fragment from + * `App\Support\DataCite\MetadataEnricher` into the base attributes. + * + * Per-key array_merge (not array_merge_recursive) so that `creators` + * and `dates` are not accidentally collapsed. + * + * @param array $attributes + * @return array + */ + private function mergeNmrEnrichment(array $attributes): array + { + try { + $enricher = app(MetadataEnricher::class); + + $fragment = match (true) { + $this instanceof Project => $enricher->forProject($this), + $this instanceof Study => $enricher->forStudy($this), + $this instanceof Dataset => $enricher->forDataset($this), + default => [], + }; + } catch (\Throwable $e) { + Log::warning('MetadataEnricher failed; emitting un-enriched DataCite payload', [ + 'model' => static::class, + 'id' => $this->id ?? null, + 'error' => $e->getMessage(), + ]); + + return $attributes; + } + + foreach (['subjects', 'descriptions', 'sizes', 'formats', 'alternateIdentifiers', 'relatedIdentifiers'] as $key) { + if (! empty($fragment[$key]) && is_array($fragment[$key])) { + $attributes[$key] = array_merge($attributes[$key] ?? [], $fragment[$key]); + } + } + + return $attributes; + } + + /** + * Build the DataCite 4.4 publisher object. Falls back to a plain string + * when no ROR is configured so the payload still validates. + * + * @return array|string + */ + private function buildPublisher() + { + $name = (string) (config('doi.publisher_name') ?? config('app.name') ?? 'nmrXiv'); + $ror = config('doi.publisher_ror'); + + if (empty($ror)) { + return $name; + } + + return [ + 'name' => $name, + 'publisherIdentifier' => $ror, + 'publisherIdentifierScheme' => 'ROR', + 'schemeUri' => 'https://ror.org', + 'lang' => 'en', + ]; + } + + /** + * Repository-as-contributor entry (DataCite 4.4 best practice for + * repository-hosted records). Returns null when not configured so we + * don't emit an anonymous HostingInstitution. + * + * @return array|null + */ + private function buildHostingInstitutionContributor(): ?array + { + $name = (string) (config('doi.publisher_name') ?? ''); + $ror = config('doi.publisher_ror'); + + if ($name === '') { + return null; + } + + $entry = [ + 'contributorType' => 'HostingInstitution', + 'name' => $name, + 'nameType' => 'Organizational', + ]; + + if (! empty($ror)) { + $entry['nameIdentifiers'] = [[ + 'nameIdentifier' => $ror, + 'nameIdentifierScheme' => 'ROR', + 'schemeUri' => 'https://ror.org', + ]]; + } + + return $entry; + } + + /** + * Point DataCite at the live JSON-LD/DataCite metadata endpoint(s) + * already exposed by nmrXiv. Satisfies FAIR-A (metadata accessible + * even when data isn't). + * + * @return list> + */ + private function buildHasMetadataLinks(): array + { + $publicId = $this->buildPublicIdentifier(); + if ($publicId === null) { + return []; + } + + $appUrl = rtrim((string) config('app.url'), '/'); + if ($appUrl === '') { + return []; + } + + return [ + [ + 'relatedIdentifier' => $appUrl.'/api/v1/schemas/datacite/'.$publicId, + 'relatedIdentifierType' => 'URL', + 'relationType' => 'HasMetadata', + 'relatedMetadataScheme' => 'DataCite', + 'schemeUri' => 'http://datacite.org/schema/kernel-4.4', + ], + [ + 'relatedIdentifier' => $appUrl.'/api/v1/schemas/bioschemas/'.$publicId, + 'relatedIdentifierType' => 'URL', + 'relationType' => 'HasMetadata', + 'relatedMetadataScheme' => 'Bioschemas', + ], + ]; + } + + /** + * @return list> + */ + private function buildIsCompiledByLinks(): array + { + $entries = []; + + $nmrium = config('doi.related_software.nmrium'); + if (! empty($nmrium)) { + $entries[] = [ + 'relatedIdentifier' => $nmrium, + 'relatedIdentifierType' => 'DOI', + 'relationType' => 'IsCompiledBy', + ]; + } + + return $entries; + } + + private function buildPublicIdentifier(): ?string + { + $raw = $this->getRawOriginal('identifier') ?? null; + if (empty($raw)) { + return null; + } + + return match (true) { + $this instanceof Project => 'P'.$raw, + $this instanceof Study => 'S'.$raw, + $this instanceof Dataset => 'D'.$raw, + default => null, + }; + } + + /** + * Strip the IsIdenticalTo entry pointing at the provisional DOI from a + * relatedIdentifiers list. The provisional record's metadata starts + * from the canonical's `getMetadata()`, which adds an IsIdenticalTo + * entry to itself — that would be a self-reference once the + * provisional record is the subject of the PUT, so we strip it. + * + * @param array> $list + * @return array> + */ + private function stripIsIdenticalTo(array $list): array + { + $provisional = $this->provisional_doi ?? null; + if (empty($provisional)) { + return $list; + } + + return array_values(array_filter( + $list, + fn ($entry) => ! (($entry['relationType'] ?? null) === 'IsIdenticalTo' + && ($entry['relatedIdentifier'] ?? null) === $provisional) + )); + } + + /** + * Append a relatedIdentifier entry to a list iff one with the same + * `relatedIdentifier + relationType` pair is not already present. + * + * @param array> $list + * @param array $entry + * @return array> + */ + private function mergeRelatedIdentifier(array $list, array $entry): array + { + foreach ($list as $existing) { + if (($existing['relatedIdentifier'] ?? null) === ($entry['relatedIdentifier'] ?? null) + && ($existing['relationType'] ?? null) === ($entry['relationType'] ?? null) + ) { + return $list; + } + } + $list[] = $entry; + + return $list; + } } diff --git a/app/Models/Molecule.php b/app/Models/Molecule.php index 84390bb0c..c4edec15a 100644 --- a/app/Models/Molecule.php +++ b/app/Models/Molecule.php @@ -34,6 +34,13 @@ class Molecule extends Model */ protected $appends = ['public_url']; + protected function casts(): array + { + return [ + 'workspace_experiment_type_counts' => 'array', + ]; + } + /** * Get the molecule identifier */ @@ -46,7 +53,7 @@ protected function identifier(): Attribute protected function getPublicUrlAttribute() { - return env('APP_URL', null).'/compound/M'.$this->getRawOriginal('identifier'); + return config('app.url').'/compound/M'.$this->getRawOriginal('identifier'); } public function samples(): BelongsToMany diff --git a/app/Models/NMRium.php b/app/Models/NMRium.php index b402ccb26..9e10f4cd2 100644 --- a/app/Models/NMRium.php +++ b/app/Models/NMRium.php @@ -19,6 +19,8 @@ class NMRium extends Model protected $fillable = [ 'nmrium_info', + 'nmriumable_id', + 'nmriumable_type', 'dataset_id', ]; diff --git a/app/Models/Project.php b/app/Models/Project.php index 8cad91ce9..3d29c19e8 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -9,13 +9,13 @@ use App\Notifications\ProjectDeletionReminderNotification; use App\Traits\CacheClear; use Auth; -use Carbon\Carbon; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Notification; use Laravel\Scout\Searchable; use Maize\Markable\Markable; @@ -42,6 +42,9 @@ class Project extends Model implements Auditable 'starred', 'location', 'is_public', + 'is_deleted', + 'is_archived', + 'status', 'obfuscationcode', 'description', 'type', @@ -57,8 +60,25 @@ class Project extends Model implements Auditable 'release_date', 'deleted_on', 'species', + 'provisional_doi', + 'provisional_doi_registered_at', + 'validation_id', + 'validation_status', + 'schema_version', ]; + /** + * The attributes that should be cast. + */ + protected function casts(): array + { + return [ + 'provisional_doi_registered_at' => 'datetime', + 'release_date' => 'datetime', + 'deleted_on' => 'datetime', + ]; + } + protected static $marks = [ Like::class, Bookmark::class, @@ -69,7 +89,7 @@ class Project extends Model implements Auditable * * @var array */ - protected $appends = ['public_url', 'private_url', 'project_photo_url', 'is_bookmarked', 'is_published']; + protected $appends = ['public_url', 'private_url', 'project_photo_url', 'is_bookmarked', 'is_published', 'provisional_doi_url']; /** * Get the URL to the project's profile photo. @@ -79,23 +99,13 @@ class Project extends Model implements Auditable public function getProjectPhotoUrlAttribute() { return $this->project_photo_path - ? Storage::disk(env('FILESYSTEM_DRIVER_PUBLIC'))->url($this->project_photo_path) + ? Storage::disk(config('filesystems.default_public'))->url($this->project_photo_path) : ''; } public function getIsPublishedAttribute() { - if ($this->is_public) { - return true; - } else { - if ($this->release_date && $this->doi) { - return Carbon::now()->startOfDay()->gte($this->release_date); - } else { - return false; - } - } - - return false; + return (bool) $this->is_public; } /** @@ -113,6 +123,28 @@ public function getIsBookmarkedAttribute() } } + /** + * Resolver URL for the provisional DOI, when set. + */ + protected function provisionalDoiUrl(): Attribute + { + return Attribute::make( + get: function () { + $doi = $this->attributes['provisional_doi'] ?? null; + if ($doi === null || $doi === '') { + return null; + } + + $host = rtrim((string) config('doi.host'), '/'); + if ($host === '' || ! str_contains($host, '://')) { + $host = 'https://doi.org'; + } + + return $host.'/'.$doi; + }, + ); + } + /** * Get the project identifier */ @@ -159,7 +191,7 @@ public function owner(): BelongsTo /** * Get all of the project's users including its owner. * - * @return \Illuminate\Support\Collection + * @return Collection */ public function allUsers() { @@ -180,7 +212,7 @@ public function users(): BelongsToMany /** * Determine if the given user belongs to the project. * - * @param \App\Models\User $user + * @param User $user * @return bool */ public function hasUser($user) @@ -253,7 +285,7 @@ public function projectInvitations(): HasMany /** * Remove the given user from the project. * - * @param \App\Models\User $user + * @param User $user * @return void */ public function removeUser($user) @@ -264,12 +296,12 @@ public function removeUser($user) protected function getPublicUrlAttribute() { // return env('APP_URL', null).'/projects/'.$this->owner->username.'/'.urlencode($this->slug); - return env('APP_URL', null).'/project/P'.$this->getRawOriginal('identifier'); + return config('app.url').'/project/P'.$this->getRawOriginal('identifier'); } protected function getPrivateUrlAttribute() { - return env('APP_URL', null).'/projects/'.urlencode($this->url); + return config('app.url').'/projects/'.urlencode($this->url); } /** @@ -352,8 +384,13 @@ public function sendNotification($notifyType, $sendTo) Notification::send($sendTo, new ProjectDeletionFailureNotification($this)); break; case 'publish': - event(new DraftProcessed($this, $sendTo)); + event(new DraftProcessed($this->fresh() ?? $this, $sendTo)); break; } } + + public function embargoReminders(): HasMany + { + return $this->hasMany(EmbargoReminder::class); + } } diff --git a/app/Models/ProjectInvitation.php b/app/Models/ProjectInvitation.php index 024c8096e..d0ec49e9f 100644 --- a/app/Models/ProjectInvitation.php +++ b/app/Models/ProjectInvitation.php @@ -2,17 +2,21 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ProjectInvitation extends Model { + use HasFactory; + /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ + 'project_id', 'email', 'role', 'message', diff --git a/app/Models/Sample.php b/app/Models/Sample.php index 596cc8d90..b4c803b66 100644 --- a/app/Models/Sample.php +++ b/app/Models/Sample.php @@ -37,4 +37,251 @@ public function study(): BelongsTo { return $this->belongsTo(Study::class, 'study_id'); } + + /** + * Build NMRium-shaped molecule entries (`{id,label,molfile}`) from the + * molecules associated with this sample. Used to hydrate NMRium when the + * saved `data.molecules` array is empty but the sample already has + * compounds attached (e.g. added via the sidebar before NMRium first + * loaded the spectra). Returns an empty array if nothing can be hydrated. + * + * @return array + */ + public function toNmriumMolecules(): array + { + $hydrated = []; + + foreach ($this->molecules()->get() as $molecule) { + $molfile = $molecule->sdf; + if (! is_string($molfile) || trim($molfile) === '') { + continue; + } + + $label = $molecule->iupac_name + ?? $molecule->name + ?? $molecule->cas + ?? $molecule->canonical_smiles + ?? $molecule->smiles + ?? ('Molecule '.$molecule->id); + + $hydrated[] = [ + 'id' => (string) ($molecule->inchi_key ?? $molecule->standard_inchi_key ?? $molecule->id), + 'label' => (string) $label, + 'molfile' => self::ensureMolfileHeader($molfile, (string) $label), + ]; + } + + return $hydrated; + } + + /** + * MOL/SDF files require exactly 3 header lines before the counts line: + * line 1: title (compound name; may be empty but the line must exist) + * line 2: generator (software/timestamp/dimensions, e.g. "RDKit 2D") + * line 3: comment (may be empty) + * line 4: counts ("nat nbond ... V2000" or " 0 0 ... V3000") + * + * Both NMRium's drawing tool and the chemistry standardize endpoint can + * emit molfiles where the title line is missing entirely, leaving only + * 2 header lines. Most parsers (and NMRium itself on reload) silently + * fail on those because they read the generator line as the title and + * then misalign every subsequent line. + * + * This method detects the V2000/V3000 counts line and, if it sits at an + * index < 3, prepends blank lines (or the supplied `$titleHint`) so the + * counts line ends up at index 3. Already-well-formed molfiles are + * returned unchanged. Non-molfile input is returned unchanged so this is + * safe to call defensively. + */ + public static function ensureMolfileHeader(string $molfile, string $titleHint = ''): string + { + if ($molfile === '') { + return $molfile; + } + + $normalized = str_replace(["\r\n", "\r"], "\n", $molfile); + $lines = explode("\n", $normalized); + + $countsLineIdx = null; + foreach ($lines as $idx => $line) { + if (preg_match('/V2000\s*$/', $line) || preg_match('/V3000\s*$/', $line)) { + $countsLineIdx = $idx; + break; + } + } + + if ($countsLineIdx === null || $countsLineIdx >= 3) { + return $molfile; + } + + $missing = 3 - $countsLineIdx; + $prepend = []; + if ($missing > 0) { + $prepend[] = $titleHint; + } + for ($i = 1; $i < $missing; $i++) { + $prepend[] = ''; + } + + return implode("\n", array_merge($prepend, $lines)); + } + + /** + * Merge sample-side molecules into a saved NMRium `data.molecules` array. + * Strategy: + * 1. Saved entries are kept in order (NMRium-drawn coordinates / labels + * win because NMRium uses `id` as its internal handle for assignments + * and spectra annotations). + * 2. When a saved entry fingerprints to the same compound as a sample + * molecule but is missing `id` or `label`, those fields are filled + * in from the sample side. NMRium silently rejects molecules with an + * empty `id`, so this enrichment is what makes user-visible structure + * rendering work for samples uploaded via the sidebar/composition UI. + * 3. Sample molecules whose fingerprint isn't represented in the saved + * list are appended at the end. + * + * Fingerprinting is structure-aware (atoms + bonds + element histogram) so + * V2000 (RDKit-standardized) and V3000 (NMRium-drawn) variants of the same + * compound collapse to one entry. + * + * @param array> $savedMolecules + * @return array> + */ + public function mergeNmriumMolecules(array $savedMolecules): array + { + $hydratedByFingerprint = []; + foreach ($this->toNmriumMolecules() as $entry) { + $hydratedByFingerprint[self::molfileFingerprint($entry['molfile'])] = $entry; + } + + $merged = []; + $seen = []; + + foreach ($savedMolecules as $entry) { + if (! is_array($entry)) { + $merged[] = $entry; + + continue; + } + + $molfile = $entry['molfile'] ?? null; + if (! is_string($molfile) || $molfile === '') { + $merged[] = $entry; + + continue; + } + + $fingerprint = self::molfileFingerprint($molfile); + $matchingHydrated = $hydratedByFingerprint[$fingerprint] ?? null; + + $savedId = isset($entry['id']) ? trim((string) $entry['id']) : ''; + $savedLabel = isset($entry['label']) ? trim((string) $entry['label']) : ''; + + if ($matchingHydrated !== null && ($savedId === '' || $savedLabel === '')) { + $entry['id'] = $savedId !== '' ? $entry['id'] : $matchingHydrated['id']; + $entry['label'] = $savedLabel !== '' ? $entry['label'] : $matchingHydrated['label']; + } + + $entry['molfile'] = self::ensureMolfileHeader( + $molfile, + isset($entry['label']) ? (string) $entry['label'] : '' + ); + + $merged[] = $entry; + $seen[$fingerprint] = true; + } + + foreach ($hydratedByFingerprint as $fingerprint => $entry) { + if (isset($seen[$fingerprint])) { + continue; + } + $merged[] = $entry; + $seen[$fingerprint] = true; + } + + return $merged; + } + + /** + * Build a structure-aware fingerprint of a MOL/SDF block for dedup so that + * the same compound serialized in different formats (V2000 vs V3000), + * different generators (Actelion vs RDKit), or with different headers + * still maps to the same key. + * + * The fingerprint is `"::"`. + * Examples: + * benzene -> "6:6:C6" + * ethanol -> "3:2:C2O1" + * methane -> "1:0:C1" + * + * This intentionally ignores stereochemistry, coordinates and connectivity + * order so it is robust to RDKit canonicalization. It can collide for + * positional isomers with the same molecular formula and ring count, which + * is acceptable for the "is this molecule already shown?" question NMRium + * needs answered. If a more rigorous comparison is ever required, prefer + * an InChIKey-based dedup populated from the chem-standardize endpoint at + * write time rather than re-parsing molfiles on every read. + */ + private static function molfileFingerprint(string $molfile): string + { + $atoms = 0; + $bonds = 0; + $elements = []; + + $lines = preg_split("/\r\n|\r|\n/", $molfile) ?: []; + $isV3000 = false; + + foreach ($lines as $line) { + if (stripos($line, 'V3000') !== false) { + $isV3000 = true; + break; + } + if (stripos($line, 'V2000') !== false) { + break; + } + } + + if ($isV3000) { + foreach ($lines as $line) { + if (preg_match('/^\s*M\s+V30\s+COUNTS\s+(\d+)\s+(\d+)/i', $line, $m)) { + $atoms = (int) $m[1]; + $bonds = (int) $m[2]; + } + if (preg_match('/^\s*M\s+V30\s+\d+\s+([A-Z][a-z]?)\s+-?\d/', $line, $m)) { + $sym = $m[1]; + $elements[$sym] = ($elements[$sym] ?? 0) + 1; + } + } + } else { + $countsLineIdx = null; + foreach ($lines as $idx => $line) { + if (preg_match('/^\s*(\d+)\s+(\d+).*V2000/', $line, $m)) { + $atoms = (int) $m[1]; + $bonds = (int) $m[2]; + $countsLineIdx = $idx; + break; + } + } + if ($countsLineIdx !== null) { + for ($i = $countsLineIdx + 1; $i <= $countsLineIdx + $atoms && $i < count($lines); $i++) { + if (preg_match('/^\s*-?\d+\.\d+\s+-?\d+\.\d+\s+-?\d+\.\d+\s+([A-Z][a-z]?)/', $lines[$i], $m)) { + $sym = $m[1]; + $elements[$sym] = ($elements[$sym] ?? 0) + 1; + } + } + } + } + + if ($atoms === 0 && empty($elements)) { + return 'raw:'.md5(preg_replace('/\s+/', ' ', trim($molfile)) ?? ''); + } + + ksort($elements); + $hist = ''; + foreach ($elements as $sym => $count) { + $hist .= $sym.$count; + } + + return $atoms.':'.$bonds.':'.$hist; + } } diff --git a/app/Models/Schemas/Study.php b/app/Models/Schemas/Study.php index c9d7b5976..05284317b 100644 --- a/app/Models/Schemas/Study.php +++ b/app/Models/Schemas/Study.php @@ -3,6 +3,14 @@ namespace App\Models\Schemas; use Spatie\SchemaOrg\BaseType; +use Spatie\SchemaOrg\Contracts\BioChemEntityContract; +use Spatie\SchemaOrg\Contracts\CreativeWorkContract; +use Spatie\SchemaOrg\Contracts\DefinedTermContract; +use Spatie\SchemaOrg\Contracts\MedicalEntityContract; +use Spatie\SchemaOrg\Contracts\OrganizationContract; +use Spatie\SchemaOrg\Contracts\PersonContract; +use Spatie\SchemaOrg\Contracts\PropertyValueContract; +use Spatie\SchemaOrg\Contracts\ThingContract; /** * A body of structured information describing some topic(s) of interest. @@ -17,7 +25,7 @@ class Study extends BaseType * in that HTML 5 provides a special mechanism for indicating authorship via * the rel tag. That is equivalent to this and may be used interchangeably. * - * @param \Spatie\SchemaOrg\Contracts\OrganizationContract|\Spatie\SchemaOrg\Contracts\OrganizationContract[]|\Spatie\SchemaOrg\Contracts\PersonContract|\Spatie\SchemaOrg\Contracts\PersonContract[] $author + * @param OrganizationContract|OrganizationContract[]|PersonContract|PersonContract[] $author * @return static * * @see https://schema.org/author @@ -60,7 +68,7 @@ public function description($description) * strings or as URL (URI) links. See [background * notes](/docs/datamodel.html#identifierBg) for more details. * - * @param \Spatie\SchemaOrg\Contracts\PropertyValueContract|\Spatie\SchemaOrg\Contracts\PropertyValueContract[]|string|string[] $identifier + * @param PropertyValueContract|PropertyValueContract[]|string|string[] $identifier * @return static * * @see https://schema.org/identifier @@ -88,7 +96,7 @@ public function name($name) * functional genomics or earth science. Those domains can also have an ontology * reference. * - * @param \Spatie\SchemaOrg\Contracts\PropertyValueContract|\Spatie\SchemaOrg\Contracts\PropertyValueContract[]|string|string[] $studyDomain + * @param PropertyValueContract|PropertyValueContract[]|string|string[] $studyDomain * @return static */ public function studyDomain($studyDomain) @@ -100,7 +108,7 @@ public function studyDomain($studyDomain) * A subject of the study, i.e. one of the medical conditions, therapies, devices, * drugs, etc. investigated by the study. * - * @param \Spatie\SchemaOrg\Contracts\BioChemEntityContract|\Spatie\SchemaOrg\Contracts\MedicalEntityContract[] $studySubject + * @param BioChemEntityContract|MedicalEntityContract[] $studySubject * @return static */ public function studySubject($studySubject) @@ -111,7 +119,7 @@ public function studySubject($studySubject) /** * The subject matter of the content. * - * @param \Spatie\SchemaOrg\Contracts\ThingContract|\Spatie\SchemaOrg\Contracts\ThingContract[] $about + * @param ThingContract|ThingContract[] $about * @return static * * @see https://schema.org/about @@ -131,7 +139,7 @@ public function about($about) * such data to be provided using those properties, rather than using the generic * property/value mechanism. * - * @param \Spatie\SchemaOrg\Contracts\PropertyValueContract|\Spatie\SchemaOrg\Contracts\PropertyValueContract[] $additionalProperty + * @param PropertyValueContract|PropertyValueContract[] $additionalProperty * @return static * * @see https://schema.org/additionalProperty @@ -145,7 +153,7 @@ public function additionalProperty($additionalProperty) * A citation or reference to another creative work, such as another * publication, web page, scholarly article, etc. * - * @param \Spatie\SchemaOrg\Contracts\CreativeWorkContract|\Spatie\SchemaOrg\Contracts\CreativeWorkContract[]|string|string[] $citation + * @param CreativeWorkContract|CreativeWorkContract[]|string|string[] $citation * @return static * * @see https://schema.org/citation @@ -159,7 +167,7 @@ public function citation($citation) * The creator/author of this CreativeWork. This is the same as the Author * property for CreativeWork. * - * @param \Spatie\SchemaOrg\Contracts\OrganizationContract|\Spatie\SchemaOrg\Contracts\OrganizationContract[]|\Spatie\SchemaOrg\Contracts\PersonContract|\Spatie\SchemaOrg\Contracts\PersonContract[] $creator + * @param OrganizationContract|OrganizationContract[]|PersonContract|PersonContract[] $creator * @return static * * @see https://schema.org/creator @@ -214,7 +222,7 @@ public function startDate($startDate) * a keywords list are typically delimited by commas, or by repeating the * property. * - * @param \Spatie\SchemaOrg\Contracts\DefinedTermContract|\Spatie\SchemaOrg\Contracts\DefinedTermContract[]|string|string[] $keywords + * @param DefinedTermContract|DefinedTermContract[]|string|string[] $keywords * @return static * * @see https://schema.org/keywords @@ -228,7 +236,7 @@ public function keywords($keywords) * TA process performed as part of an experiment or wider study, i.e. intentionally * designed. These processes can have ontology URL attached to. * - * @param \Spatie\SchemaOrg\Contracts\PropertyValueContract|\Spatie\SchemaOrg\Contracts\PropertyValueContract[]|string|string[] $studyProcess + * @param PropertyValueContract|PropertyValueContract[]|string|string[] $studyProcess * @return static */ public function studyProcess($studyProcess) diff --git a/app/Models/Study.php b/app/Models/Study.php index 8de2b3fb9..9b21401f3 100644 --- a/app/Models/Study.php +++ b/app/Models/Study.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\MorphOne; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; use Laravel\Scout\Searchable; @@ -38,6 +39,7 @@ class Study extends Model implements Auditable 'starred', 'location', 'is_public', + 'is_archived', 'obfuscationcode', 'description', 'type', @@ -60,6 +62,13 @@ class Study extends Model implements Auditable 'external_url', 'processing_logs', 'tracking_item_name', + 'doi', + 'identifier', + 'validation_id', + 'metadata_bagit_generation_status', + 'metadata_bagit_generation_logs', + 'has_nmrium', + 'has_nmredata', ]; /** @@ -68,10 +77,13 @@ class Study extends Model implements Auditable protected function casts(): array { return [ - 'authors' => 'array', 'citations' => 'array', 'molecules' => 'array', 'processing_logs' => 'array', + 'metadata_bagit_generation_logs' => 'array', + 'starred' => 'boolean', + 'is_public' => 'boolean', + 'is_archived' => 'boolean', ]; } @@ -131,7 +143,7 @@ protected function identifier(): Attribute public function getStudyPhotoUrlAttribute() { return $this->study_photo_path - ? Storage::disk(env('FILESYSTEM_DRIVER_PUBLIC'))->url($this->study_photo_path) + ? Storage::disk(config('filesystems.default_public'))->url($this->study_photo_path) : ''; } @@ -173,12 +185,12 @@ public function project(): BelongsTo protected function getPublicUrlAttribute() { // return env('APP_URL', null).'/projects/'.$this->owner->username.'/'.urlencode($this->project->slug).'?tab=study&id='.$this->slug; - return env('APP_URL', null).'/sample/S'.$this->getRawOriginal('identifier'); + return config('app.url').'/sample/S'.$this->getRawOriginal('identifier'); } protected function getPrivateUrlAttribute() { - return env('APP_URL', null).'/studies/'.urlencode($this->url); + return config('app.url').'/studies/'.urlencode($this->url); } public function draft(): BelongsTo @@ -261,7 +273,7 @@ public function owner(): BelongsTo /** * Get all of the study's users including its owner. * - * @return \Illuminate\Support\Collection + * @return Collection */ public function allUsers() { @@ -296,7 +308,7 @@ public function molecules() /** * Remove the given user from the study. * - * @param \App\Models\User $user + * @param User $user * @return void */ public function removeUser($user) @@ -311,9 +323,7 @@ public function removeUser($user) */ public function shouldBeSearchable() { - if ($this->is_public && ! $this->is_archived) { - return true; - } + return $this->is_public && ! $this->is_archived; } /** @@ -335,6 +345,33 @@ public function license(): BelongsTo return $this->belongsTo(License::class, 'license_id'); } + /** + * Get all of the authors that belong to the study. + */ + public function studyAuthors(): BelongsToMany + { + return $this->belongsToMany(Author::class) + ->withPivot('contributor_type', 'sort_order')->orderBy('sort_order', 'asc'); + } + + /** + * Normalized citations (pivot `citation_study`). Named `linkedCitations` to avoid clashing with the JSON `citations` attribute. + */ + public function linkedCitations(): BelongsToMany + { + return $this->belongsToMany(Citation::class)->withTimestamps(); + } + + /** + * Accessor for authors attribute (alias for studyAuthors relationship). + */ + protected function authors(): Attribute + { + return Attribute::make( + get: fn () => $this->studyAuthors, + ); + } + public function scopeFilter($query, array $filters) { $query->when($filters['search'] ?? null, function ($query, $search) { diff --git a/app/Models/StudyInvitation.php b/app/Models/StudyInvitation.php index b2882e400..245084341 100644 --- a/app/Models/StudyInvitation.php +++ b/app/Models/StudyInvitation.php @@ -2,17 +2,21 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class StudyInvitation extends Model { + use HasFactory; + /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ + 'study_id', 'email', 'role', 'message', diff --git a/app/Models/TeamInvitation.php b/app/Models/TeamInvitation.php index 90294d426..4c78cff45 100644 --- a/app/Models/TeamInvitation.php +++ b/app/Models/TeamInvitation.php @@ -2,18 +2,22 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Laravel\Jetstream\Jetstream; use Laravel\Jetstream\TeamInvitation as JetstreamTeamInvitation; class TeamInvitation extends JetstreamTeamInvitation { + use HasFactory; + /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ + 'team_id', 'email', 'role', 'invited_by', diff --git a/app/Models/Ticker.php b/app/Models/Ticker.php index 1afaa3434..f3ab71a7d 100644 --- a/app/Models/Ticker.php +++ b/app/Models/Ticker.php @@ -8,4 +8,9 @@ class Ticker extends Model { use HasFactory; + + protected $fillable = [ + 'type', + 'index', + ]; } diff --git a/app/Models/User.php b/app/Models/User.php index 466e0763e..0ebf90e62 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -31,11 +31,11 @@ class User extends Authenticatable implements MustVerifyEmail /** * The attributes that are mass assignable. - * + /** * @var array */ protected $fillable = [ - 'name', 'first_name', 'last_name', 'username', 'email', 'password', 'onboarded', 'orcid_id', 'affiliation', + 'name', 'first_name', 'last_name', 'username', 'email', 'password', 'onboarded', 'orcid_id', 'affiliation', 'ror_id', ]; /** diff --git a/app/Models/Validation.php b/app/Models/Validation.php index 3266d24a1..3ab56f48d 100644 --- a/app/Models/Validation.php +++ b/app/Models/Validation.php @@ -2,6 +2,7 @@ namespace App\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -81,10 +82,13 @@ public function project(): HasOne return $this->hasOne(Project::class); } - public function process() + public function process(bool $forceSamplesMode = false): void { - $project = $this->project; - $project->load('tags', 'authors', 'citations'); + if (! $project = $this->project) { + return; + } + + $project->load('tags', 'authors', 'citations', 'draft'); $report = $this->report; @@ -92,164 +96,253 @@ public function process() $warnings = []; $errors = []; + $samplesMode = $forceSamplesMode + || ($project->draft && $project->draft->project_enabled === false); + $schema_version = $project->schema_version ? $project->schema_version : config('validations.default'); $project->schema_version = $schema_version; $rules = config('validations.'.$schema_version); - if ($project) { + $values = [ + 'title' => $project->name, + 'description' => $project->description, + 'keywords' => $project->tags->pluck('id')->toArray(), + 'citations' => $project->citations->pluck('id')->toArray(), + 'authors' => $project->authors->pluck('id')->toArray(), + 'license' => $project->license, + 'image' => $project->project_photo_path, + ]; + + $project_rules = $rules['project']; + + $validator = Validator::make($values, $project_rules); + + if ($validator->fails()) { + $errors = $validator->errors()->getMessages(); + foreach ($project_rules as $key => $value) { + if (array_key_exists($key, $errors)) { + $report['project'][$key] = $samplesMode + ? 'true|skipped-samples-mode' + : 'false|'.$project_rules[$key]; + if (! $samplesMode && strpos($project_rules[$key], 'required') !== false) { + $status = false; + } + } else { + $report['project'][$key] = 'true|'.$project_rules[$key]; + } + } + } else { + foreach ($project_rules as $key => $value) { + $report['project'][$key] = 'true|'.$project_rules[$key]; + } + } + + $studies = $project->studies; + + $studiesValidation = []; + + foreach ($studies as $study) { + $sstatus = true; + $study->load(['datasets', 'sample.molecules', 'tags']); + $studyReport = [ + 'name' => $study->name, + 'id' => $study->id, + ]; + $values = [ - 'title' => $project->name, - 'description' => $project->description, - 'keywords' => $project->tags->pluck('id')->toArray(), - 'citations' => $project->citations->pluck('id')->toArray(), - 'authors' => $project->authors->pluck('id')->toArray(), - 'license' => $project->license, - 'image' => $project->project_photo_path, + 'title' => $study->name, + 'description' => $study->description, + 'keywords' => $study->tags->pluck('id')->toArray(), + 'composition' => $study->sample->molecules->pluck('id')->toArray(), + 'nmrium_info' => $study->has_nmrium ? $study->has_nmrium : null, + 'sample' => $study->sample, + 'molecules' => $study->sample->molecules->pluck('id')->toArray(), ]; - $project_rules = $rules['project']; + $study_rules = $rules['study']; - $validator = Validator::make($values, $project_rules); + $validator = Validator::make($values, $study_rules); if ($validator->fails()) { $errors = $validator->errors()->getMessages(); - foreach ($project_rules as $key => $value) { + foreach ($study_rules as $key => $value) { if (array_key_exists($key, $errors)) { - $report['project'][$key] = 'false|'.$project_rules[$key]; - if (strpos($project_rules[$key], 'required') !== false) { + $studyReport[$key] = 'false|'.$study_rules[$key]; + if (strpos($study_rules[$key], 'required') !== false) { + $sstatus = false; $status = false; } } else { - $report['project'][$key] = 'true|'.$project_rules[$key]; + $studyReport[$key] = 'true|'.$study_rules[$key]; } } } else { - foreach ($project_rules as $key => $value) { - $report['project'][$key] = 'true|'.$project_rules[$key]; + foreach ($study_rules as $key => $value) { + $studyReport[$key] = 'true|'.$study_rules[$key]; } } - $studies = $project->studies; - - $studiesValidation = []; + $datasets = $study->datasets; - foreach ($studies as $study) { - $sstatus = true; - $study->load(['datasets', 'sample.molecules', 'tags']); - $studyReport = [ - 'name' => $study->name, - 'id' => $study->id, + $datasetsValidation = []; + foreach ($datasets as $dataset) { + $dstatus = true; + $datasetReport = [ + 'name' => $dataset->name, + 'id' => $dataset->id, ]; + $instrumentType = $dataset->fsObject ? $dataset->fsObject->instrument_type : null; + + if (! $instrumentType) { + // check if children have instrument_type + $children = $dataset->fsObject ? $dataset->fsObject->children : null; + if ($children) { + foreach ($children as $child) { + $instrumentType = $child->instrument_type; + if ($instrumentType) { + break; + } + } + } + } + $values = [ - 'title' => $study->name, - 'description' => $study->description, - 'keywords' => $study->tags->pluck('id')->toArray(), - 'composition' => $study->sample->molecules->pluck('id')->toArray(), - 'nmrium_info' => $study->has_nmrium ? $study->has_nmrium : null, - 'sample' => $study->sample, - 'molecules' => $study->sample->molecules->pluck('id')->toArray(), + 'files' => $instrumentType ? $instrumentType : null, + 'nmrium_info' => ($dataset->has_nmrium) ? $dataset->has_nmrium : null, + 'assay' => $dataset->assay, + // The validator rule for `assignments` is `array|min:1`, + // so we only pass the actual saved entries (atom_peaks) + // when present, plus an `acs` token when the user pasted + // a free-form ACS string. Falling back to `has_nmrium` + // here would have been a lie - it lets unfilled samples + // pass validation just because spectra were imported. + 'assignments' => $this->assignmentsValueFor($dataset), ]; - $study_rules = $rules['study']; + $dataset_rules = $rules['dataset']; - $validator = Validator::make($values, $study_rules); + $validator = Validator::make($values, $dataset_rules); if ($validator->fails()) { $errors = $validator->errors()->getMessages(); - foreach ($study_rules as $key => $value) { + foreach ($dataset_rules as $key => $value) { if (array_key_exists($key, $errors)) { - $studyReport[$key] = 'false|'.$study_rules[$key]; - if (strpos($study_rules[$key], 'required') !== false) { + $datasetReport[$key] = 'false|'.$dataset_rules[$key]; + if (strpos($dataset_rules[$key], 'required') !== false) { + $dstatus = false; $sstatus = false; $status = false; } } else { - $studyReport[$key] = 'true|'.$study_rules[$key]; + $datasetReport[$key] = 'true|'.$dataset_rules[$key]; } } } else { - foreach ($study_rules as $key => $value) { - $studyReport[$key] = 'true|'.$study_rules[$key]; + foreach ($dataset_rules as $key => $value) { + $datasetReport[$key] = 'true|'.$dataset_rules[$key]; } } - $datasets = $study->datasets; - - $datasetsValidation = []; - foreach ($datasets as $dataset) { - $dstatus = true; - $datasetReport = [ - 'name' => $dataset->name, - 'id' => $dataset->id, - ]; - - $instrumentType = $dataset->fsObject ? $dataset->fsObject->instrument_type : null; - - if (! $instrumentType) { - // check if children have instrument_type - $children = $dataset->fsObject ? $dataset->fsObject->children : null; - if ($children) { - foreach ($children as $child) { - $instrumentType = $child->instrument_type; - if ($instrumentType) { - break; - } - } - } - } + $datasetReport['status'] = $dstatus; - $values = [ - 'files' => $instrumentType ? $instrumentType : null, - 'nmrium_info' => ($dataset->has_nmrium) ? $dataset->has_nmrium : null, - 'assay' => $dataset->assay, - 'assignments' => ($dataset->has_nmrium) ? $dataset->has_nmrium : null, - ]; - - $dataset_rules = $rules['dataset']; - - $validator = Validator::make($values, $dataset_rules); - - if ($validator->fails()) { - $errors = $validator->errors()->getMessages(); - foreach ($dataset_rules as $key => $value) { - if (array_key_exists($key, $errors)) { - $datasetReport[$key] = 'false|'.$dataset_rules[$key]; - if (strpos($dataset_rules[$key], 'required') !== false) { - $dstatus = false; - $sstatus = false; - $status = false; - } - } else { - $datasetReport[$key] = 'true|'.$dataset_rules[$key]; - } - } + array_push($datasetsValidation, $datasetReport); + } + $studyReport['status'] = $sstatus; + $studyReport['datasets'] = $datasetsValidation; + + array_push($studiesValidation, $studyReport); + } + + // Validate citations + $citations = $project->citations; + $citationsValidation = []; + $citationsStatus = $citations && $citations->isNotEmpty(); + $shouldValidateCitationDoi = ! $samplesMode; + + if ($shouldValidateCitationDoi && $project->release_date) { + $shouldValidateCitationDoi = Carbon::parse($project->release_date)->lessThanOrEqualTo(now()); + } + + if ($citations && $citations->isNotEmpty()) { + foreach ($citations as $citation) { + $citationReport = [ + 'name' => $citation->title ?? 'Untitled', + 'id' => $citation->id, + ]; + + if ($shouldValidateCitationDoi) { + // Check if DOI is present only for current/past release date projects. + $hasDoi = is_string($citation->doi) && trim($citation->doi) !== ''; + + if ($hasDoi) { + $citationReport['doi'] = 'true|required'; } else { - foreach ($dataset_rules as $key => $value) { - $datasetReport[$key] = 'true|'.$dataset_rules[$key]; - } + $citationReport['doi'] = 'false|required'; + $citationsStatus = false; // Citation validation failed } - $datasetReport['status'] = $dstatus; - - array_push($datasetsValidation, $datasetReport); + $citationReport['status'] = $hasDoi; + } else { + $citationReport['doi'] = $samplesMode + ? 'true|skipped-samples-mode' + : 'true|skipped-future-release'; + $citationReport['status'] = true; } - $studyReport['status'] = $sstatus; - $studyReport['datasets'] = $datasetsValidation; - array_push($studiesValidation, $studyReport); + $citationsValidation[] = $citationReport; } + } + + // In samples mode, citations are optional and DOIs are not enforced. + if ($samplesMode) { + $report['project']['citations'] = 'true|optional'; + } elseif ($citationsStatus) { + $report['project']['citations'] = 'true|required'; + } else { + $report['project']['citations'] = 'false|required'; + $status = false; // Propagate to project status + } + + // Store detailed citations validation data separately + $report['project']['citations_detail'] = $citationsValidation; + + $report['project']['studies'] = $studiesValidation; + + if ($samplesMode) { + $status = self::samplesModePublishPasses($report); + } + + $report['project']['status'] = $status; + $project->validation_status = $status; + $project->save(); + + $this->report = $this->sanitizeUnicodeInReport($report); + $this->save(); + } + + /** + * Whether a validation report allows publishing individual samples (studies only). + * + * @param array $report + */ + public static function samplesModePublishPasses(array $report): bool + { + $studies = $report['project']['studies'] ?? []; - $report['project']['studies'] = $studiesValidation; - $report['project']['status'] = $status; - $project->validation_status = $status; - $project->save(); + if ($studies === []) { + return false; + } - $this->report = $this->sanitizeUnicodeInReport($report); - $this->save(); + foreach ($studies as $study) { + if (! ($study['status'] ?? false)) { + return false; + } } + + return true; } /** @@ -302,4 +395,30 @@ private function recursiveUnicodeSanitize($data) return $data; } + + /** + * Build the value passed to the dataset's `assignments` validation rule + * (`array|min:1`). We satisfy `min:1` whenever the user has either + * pasted a non-empty ACS string OR added at least one atom_peaks row. + * Returns null when nothing is set so the rule fails cleanly and the + * UI sees `assignments: false|array|min:1` in the report. + */ + protected function assignmentsValueFor(Dataset $dataset): ?array + { + $a = $dataset->assignments; + if (! is_array($a)) { + return null; + } + $entries = []; + if (! empty($a['acs']) && trim((string) $a['acs']) !== '') { + $entries[] = ['type' => 'acs']; + } + if (! empty($a['atom_peaks']) && is_array($a['atom_peaks'])) { + foreach ($a['atom_peaks'] as $row) { + $entries[] = $row; + } + } + + return $entries === [] ? null : $entries; + } } diff --git a/app/Notifications/EmbargoPublicationFailedNotification.php b/app/Notifications/EmbargoPublicationFailedNotification.php new file mode 100644 index 000000000..7da5040a8 --- /dev/null +++ b/app/Notifications/EmbargoPublicationFailedNotification.php @@ -0,0 +1,47 @@ +project, + $this->reason, + $this->validation, + $this->exceptionClass, + $this->admin, + ))->to($notifiable->email); + } + + public function toArray($notifiable): array + { + return [ + // + ]; + } +} diff --git a/app/Notifications/EmbargoReleaseReminderNotification.php b/app/Notifications/EmbargoReleaseReminderNotification.php new file mode 100644 index 000000000..3c2bc2631 --- /dev/null +++ b/app/Notifications/EmbargoReleaseReminderNotification.php @@ -0,0 +1,61 @@ +project = $project; + $this->daysUntilRelease = $daysUntilRelease; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + */ + public function via($notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + */ + public function toMail($notifiable): Mailable + { + return (new EmbargoReleaseReminderMailable($this->project, $this->daysUntilRelease))->to($notifiable->email); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + */ + public function toArray($notifiable): array + { + return [ + // + ]; + } +} diff --git a/app/Observers/FileSystemObjectObserver.php b/app/Observers/FileSystemObjectObserver.php new file mode 100644 index 000000000..d3a3d5973 --- /dev/null +++ b/app/Observers/FileSystemObjectObserver.php @@ -0,0 +1,228 @@ + + */ + private const STRUCTURAL_FIELDS = [ + 'name', + 'path', + 'relative_url', + 'type', + 'parent_id', + 'file_size', + 'checksum_md5', + 'checksum_sha256', + 'status', + 'is_deleted', + 'is_archived', + ]; + + /** + * Handle the FileSystemObject "created" event. + */ + public function created(FileSystemObject $fileSystemObject): void + { + $this->invalidateOwningStudy($fileSystemObject, 'created'); + } + + /** + * Handle the FileSystemObject "updated" event. + */ + public function updated(FileSystemObject $fileSystemObject): void + { + $changed = array_intersect(self::STRUCTURAL_FIELDS, array_keys($fileSystemObject->getChanges())); + if (empty($changed)) { + return; + } + + $this->invalidateOwningStudy($fileSystemObject, 'updated:'.implode(',', $changed)); + } + + /** + * Handle the FileSystemObject "deleted" event. + */ + public function deleted(FileSystemObject $fileSystemObject): void + { + $this->invalidateOwningStudy($fileSystemObject, 'deleted'); + } + + /** + * Public entry point used by code paths that delete or change FSOs in + * ways Eloquent observers don't see (most importantly + * FileSystemObjectService::deleteFileSystemObject, which uses a mass + * `Builder::delete()` that intentionally bypasses model events). + * + * Callers are expected to invoke this BEFORE the bulk operation, while + * the FSO subtree is still walkable from `$fso` — otherwise resolveStudy + * would lose the parent chain and we'd silently fail to invalidate. + */ + public function invalidateForExternalChange(FileSystemObject $fso, string $reason): void + { + $this->invalidateOwningStudy($fso, $reason); + } + + /** + * Resolve the Study that owns this FileSystemObject (if any), reset its + * cached archive, and queue a rebuild so the next download / NMRium auto- + * import sees the latest files. + * + * We intentionally do NOT short-circuit when `download_url` / `has_nmrium` + * are already empty — a follow-up file change while an ArchiveStudy job + * is still in flight must still enqueue a fresh dispatch, otherwise the + * second batch of files never makes it into the archive. + */ + private function invalidateOwningStudy(FileSystemObject $fso, string $reason): void + { + $study = $this->resolveStudy($fso); + + if (! $study) { + return; + } + + $previousUrl = $study->download_url; + $hadNmriumImport = (bool) $study->has_nmrium; + + // Delegate the Study-side reset to the shared service so the right- + // click "Refresh" action (ResetSampleFolder) and this observer- + // driven path stay bit-identical. Without that, file-upload / + // file-delete invalidations produce a different post-state than the + // explicit reset, which is exactly what bit us before + // (stale `internal_status` causing autoImport to race ArchiveStudy). + $this->resetStudyCachedState->forStudy($study); + + // Clear `model_type='study'` on the sample-folder FSO so that + // FileSystemController::processFolder re-walks it during the next + // ProcessDraft run. Without this, processFolder skips folders that + // already have a `model_type` set and the freshly uploaded files + // never get re-classified into the study. + $this->clearSampleFolderTag($study, $fso); + + Log::info('FileSystemObjectObserver: invalidated study archive', [ + 'study_id' => $study->id, + 'fso_id' => $fso->id, + 'reason' => $reason, + 'previous_url' => $previousUrl, + 'previous_has_nmrium' => $hadNmriumImport, + ]); + + $this->dispatchArchiveRebuild($study); + } + + /** + * Clear the `model_type='study'` tag on the FSO that anchors the study + * (Study::fs_id), unless the change originated from that very FSO and + * the tag has already been cleared (avoids redundant writes from the + * recursive observer firing). + */ + private function clearSampleFolderTag(Study $study, FileSystemObject $changed): void + { + if (! $study->fs_id) { + return; + } + + $folder = $study->fs_id === $changed->id + ? $changed + : FileSystemObject::find($study->fs_id); + + if (! $folder || $folder->model_type !== 'study') { + return; + } + + $folder->model_type = null; + $folder->is_processed = false; + $folder->saveQuietly(); + } + + /** + * Dispatch ArchiveStudy for the study's project so the zip is rebuilt + * from the current FileSystemObjects. ArchiveStudy is unique per project + * (uniqueId = project_id, uniqueFor = 4h), so rapid-fire uploads collapse + * into a single rebuild. + */ + private function dispatchArchiveRebuild(Study $study): void + { + $project = $study->project; + + if (! $project) { + return; + } + + try { + ArchiveStudy::dispatch($project); + + Log::info('embargo_publish_trace', [ + 'stage' => 'file_system_object_observer_dispatch_archive_study', + 'project_id' => $project->id, + 'study_id' => $study->id, + ]); + } catch (\Throwable $e) { + Log::warning('FileSystemObjectObserver: failed to dispatch ArchiveStudy', [ + 'study_id' => $study->id, + 'project_id' => $project->id, + 'error' => $e->getMessage(), + ]); + } + } + + /** + * Walk up the FileSystemObject tree (and check Study::fs_id) to find the + * Study this object belongs to. + */ + private function resolveStudy(FileSystemObject $fso): ?Study + { + if ($fso->study_id) { + return Study::find($fso->study_id); + } + + $study = Study::where('fs_id', $fso->id)->first(); + if ($study) { + return $study; + } + + $current = $fso->parent; + $depth = 0; + + while ($current && $depth < 32) { + if ($current->study_id) { + return Study::find($current->study_id); + } + + $study = Study::where('fs_id', $current->id)->first(); + if ($study) { + return $study; + } + + $current = $current->parent; + $depth++; + } + + return null; + } +} diff --git a/app/Policies/ProjectPolicy.php b/app/Policies/ProjectPolicy.php index 4abc9afde..b61b8f146 100644 --- a/app/Policies/ProjectPolicy.php +++ b/app/Policies/ProjectPolicy.php @@ -72,6 +72,20 @@ public function updateProject(User $user, Project $project) return $user->canUpdateProject($project); } + /** + * Determine whether the user can publish the model. + * + * @return mixed + */ + public function publishProject(User $user, Project $project) + { + if ($project->is_public || $project->is_archived || $project->is_deleted) { + return false; + } + + return $user->canUpdateProject($project); + } + /** * Determine whether the user can delete the model. * diff --git a/app/Policies/StudyPolicy.php b/app/Policies/StudyPolicy.php index 079c70b8d..87da593c8 100644 --- a/app/Policies/StudyPolicy.php +++ b/app/Policies/StudyPolicy.php @@ -35,12 +35,16 @@ public function viewAny(User $user): bool * * @return mixed */ - public function viewStudy(User $user, Study $study) + public function viewStudy(?User $user, Study $study) { - if (is_null($user) && $study->is_public) { + if ($study->is_public) { return true; } + if (is_null($user)) { + return false; + } + return $user->belongsToStudy($study); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 6ff96d88d..474ba212b 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,10 @@ namespace App\Providers; +use App\Services\FileIntegrityService; +use App\Services\FileSystemObjectService; +use App\Services\PathGeneratorService; +use App\Services\StorageSignedUrlService; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Event; @@ -42,10 +46,10 @@ class AppServiceProvider extends ServiceProvider public function register(): void { // Register filesystem services - $this->app->singleton(\App\Services\PathGeneratorService::class); - $this->app->singleton(\App\Services\StorageSignedUrlService::class); - $this->app->singleton(\App\Services\FileIntegrityService::class); - $this->app->bind(\App\Services\FileSystemObjectService::class); + $this->app->singleton(PathGeneratorService::class); + $this->app->singleton(StorageSignedUrlService::class); + $this->app->singleton(FileIntegrityService::class); + $this->app->bind(FileSystemObjectService::class); } /** diff --git a/app/Providers/CASServiceProvider.php b/app/Providers/CASServiceProvider.php new file mode 100644 index 000000000..572cae577 --- /dev/null +++ b/app/Providers/CASServiceProvider.php @@ -0,0 +1,40 @@ +app->bind(CASService::class, function ($app) { + $provider = config('services.cas.provider'); + if ($provider === 'CAS_CommonChemistry') { + return new CommonChemistry; + } + throw new \Exception('Invalid CAS provider: '.$provider); + }); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index fbef8d473..e1aaf21ef 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -6,8 +6,10 @@ use App\Events\StudyPublish; use App\Listeners\SendDraftProcessedNotification; use App\Listeners\StudyPublish as StudyPublishListener; +use App\Services\Socialite\NFDIAAI\Provider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; +use SocialiteProviders\Manager\SocialiteWasCalled; class EventServiceProvider extends ServiceProvider { @@ -39,13 +41,9 @@ public function register(): void public function boot(): void { parent::boot(); - - // Manually register event listeners as fallback - Event::listen(DraftProcessed::class, SendDraftProcessedNotification::class); - Event::listen(StudyPublish::class, StudyPublishListener::class); - Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { + Event::listen(function (SocialiteWasCalled $event) { // Canonical slug for NFDI AAI provider is 'regapp' (matches IdP registered callback URI) - $event->extendSocialite('regapp', \App\Services\Socialite\NFDIAAI\Provider::class); + $event->extendSocialite('regapp', Provider::class); }); } } diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php index dc756afdc..54e2a2f28 100644 --- a/app/Providers/HorizonServiceProvider.php +++ b/app/Providers/HorizonServiceProvider.php @@ -45,10 +45,10 @@ public function boot(): void * Access is restricted to users with 'super-admin' or 'developer' roles * to ensure only authorized personnel can monitor and manage job queues. */ - protected function gate(): void + protected function gate() { Gate::define('viewHorizon', function ($user) { - return $user->hasAnyRole(['super-admin', 'developer']); + return $user !== null && $user->hasAnyRole(['super-admin', 'developer']); }); } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index b2b03e02a..8ed782052 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -73,5 +73,23 @@ protected function configureRateLimiting(): void RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); + + // Support bubble rate limiting to prevent spam + RateLimiter::for('support-bubble', function (Request $request) { + // Skip rate limiting in testing environment + if (app()->environment('testing')) { + return Limit::none(); + } + + return [ + // Allow 5 submissions per minute per IP + Limit::perMinute(1)->by($request->ip()), + // Allow 20 submissions per hour per IP + Limit::perHour(5)->by($request->ip()), + // Allow 50 submissions per day per IP + Limit::perDay(20)->by($request->ip()), + + ]; + }); } } diff --git a/app/Services/AuthorService.php b/app/Services/AuthorService.php deleted file mode 100644 index e9c707402..000000000 --- a/app/Services/AuthorService.php +++ /dev/null @@ -1,145 +0,0 @@ -load('authors'); - - $processedAuthors = []; - - foreach ($authors as $authorData) { - $this->validateAuthorData($authorData); - - $familyName = $authorData['family_name']; - $givenName = $authorData['given_name']; - - if (! is_null($familyName) && ! is_null($givenName)) { - $author = $this->findOrCreateAuthor($project, $authorData, $familyName, $givenName); - $author->contributor_type = $authorData['contributor_type'] ?? 'Researcher'; - $processedAuthors[] = $author; - } - } - - // Use database transaction for bulk operations - DB::transaction(function () use ($project, $processedAuthors): void { - $this->updater->attachAuthor($project, $processedAuthors); - }); - - // Reload the relationship to get fresh pivot data - $project->load('authors'); - - return $processedAuthors; - } - - /** - * Remove author from project. - */ - public function removeAuthorFromProject(Project $project, int $authorId): void - { - DB::transaction(function () use ($project, $authorId): void { - $this->updater->detachAuthor($project, $authorId); - }); - } - - /** - * Update contributor type for an author in a project. - */ - public function updateContributorType(Project $project, int $authorId, string $role): bool - { - $contributorTypes = Config::get('doi.'.Config::get('doi.default').'.contributor_types'); - - if (! in_array($role, $contributorTypes)) { - return false; - } - - $this->updater->updateContributorType($project, $authorId, $role); - - return true; - } - - /** - * Validate author data against validation rules. - * - * - * @throws \Illuminate\Validation\ValidationException - */ - private function validateAuthorData(array $authorData): void - { - Validator::make($authorData, [ - 'given_name' => ['required', 'string', 'max:255'], - 'family_name' => ['required', 'string', 'max:255'], - 'title' => ['nullable', 'string', 'max:100'], - 'email_id' => ['nullable', 'email', 'max:320'], - 'orcid_id' => ['nullable', 'string', 'max:19'], - 'affiliation' => ['nullable', 'string', 'max:500'], - 'contributor_type' => ['nullable', 'string', 'max:50'], - ])->validate(); - } - - /** - * Find existing author for project or create new one. - */ - private function findOrCreateAuthor(Project $project, array $authorData, string $familyName, string $givenName): Author - { - $existingAuthor = Author::query() - ->whereHas('projects', function ($query) use ($project): void { - $query->where('projects.id', $project->id); - }) - ->where('family_name', $familyName) - ->where('given_name', $givenName) - ->first(); - - if ($existingAuthor) { - $existingAuthor->update($this->prepareAuthorAttributes($authorData, $familyName, $givenName)); - - return $existingAuthor; - } - - return Author::create($this->prepareAuthorAttributes($authorData, $familyName, $givenName)); - } - - /** - * Prepare author attributes for create or update operations. - */ - private function prepareAuthorAttributes(array $authorData, string $familyName, string $givenName): array - { - return [ - 'title' => $authorData['title'] ?? null, - 'given_name' => $givenName, - 'family_name' => $familyName, - 'orcid_id' => $authorData['orcid_id'] ?? null, - 'email_id' => $authorData['email_id'] ?? null, - 'affiliation' => $authorData['affiliation'] ?? null, - ]; - } -} diff --git a/app/Services/CAS/CASService.php b/app/Services/CAS/CASService.php new file mode 100644 index 000000000..3eced8a62 --- /dev/null +++ b/app/Services/CAS/CASService.php @@ -0,0 +1,16 @@ + Config::get('services.cas.base_url'), + 'api_token' => Config::get('services.cas.api_token'), + ]; + } + + /** + * Fetch detailed information for a given CAS registry number + */ + public function getCASDetails(string $casNumber): array + { + try { + $config = $this->getApiConfig(); + + $response = Http::timeout(self::REQUEST_TIMEOUT) + ->withHeaders([ + 'X-API-KEY' => $config['api_token'], + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ]) + ->get("{$config['base_url']}/detail", [ + 'cas_rn' => $casNumber, + ]); + + if ($response->successful()) { + $data = $response->json(); + + // Ensure we have valid array data + if (is_array($data)) { + return $data; + } + + throw new \Exception('Invalid response format from CAS API'); + } + + throw new \Exception('Unable to retrieve CAS details. Please verify the CAS number and try again.'); + } catch (\Exception $e) { + throw new \Exception('Unable to retrieve CAS details. Please verify the CAS number and try again.'); + } + } + + /** + * Search for CAS registry number using SMILES molecular structure notation + */ + public function searchCASBySmiles(string $smiles): ?string + { + try { + $config = $this->getApiConfig(); + + $response = Http::timeout(self::REQUEST_TIMEOUT) + ->withHeaders([ + 'X-API-KEY' => $config['api_token'], + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ]) + ->get("{$config['base_url']}/search", [ + 'q' => $smiles, + ]); + + if ($response->successful()) { + $data = $response->json(); + + if (isset($data['count']) && $data['count'] > 0 && isset($data['results'][0]['rn'])) { + return $data['results'][0]['rn']; + } + } + + return null; + + } catch (\Exception $e) { + return null; + } + } +} diff --git a/app/Services/DOI/DOIService.php b/app/Services/DOI/DOIService.php index ce2a3ff86..48e231651 100644 --- a/app/Services/DOI/DOIService.php +++ b/app/Services/DOI/DOIService.php @@ -15,4 +15,27 @@ public function updateDOI($doi, $attributes = []); public function deleteDOI($doi); public function getDOIActivity($doi); + + /** + * Register a DataCite DOI using an exact, pre-built DOI string. + * + * @return array + */ + public function createCustomDOI(string $doi, array $metadata = []): array; + + /** + * Fetch the `relatedIdentifiers` array currently stored on a DataCite record. + * + * @return array> + */ + public function getRelatedIdentifiers(string $doi): array; + + /** + * Replace the `relatedIdentifiers` array (and optionally the `url`) + * on an existing DataCite record. + * + * @param array> $relatedIdentifiers + * @return array + */ + public function putRelatedIdentifiers(string $doi, array $relatedIdentifiers, ?string $url = null): array; } diff --git a/app/Services/DOI/DataCite.php b/app/Services/DOI/DataCite.php index db3b12309..ea6b27b3c 100644 --- a/app/Services/DOI/DataCite.php +++ b/app/Services/DOI/DataCite.php @@ -5,6 +5,8 @@ use Config; use GuzzleHttp\Client; use GuzzleHttp\RequestOptions; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Storage; class DataCite implements DOIService { @@ -20,6 +22,15 @@ public function __construct() $this->prefix = Config::get('doi.'.Config::get('doi.default').'.prefix'); } + /** + * Allow tests to inject a pre-built Guzzle client (for `MockHandler`) + * without going through the network. + */ + public function setHttpClient(Client $client): void + { + $this->client = $client; + } + /** * Returns a list of DOIs */ @@ -70,6 +81,124 @@ public function createDOI($suffix, $metadata = []) return json_decode($contents, true); } + /** + * Register a DataCite DOI using an exact, pre-built DOI string. + * + * Unlike `createDOI`, this does NOT compose the DOI from the configured + * prefix and `app.name` — it trusts the caller's value verbatim. Used + * by `HasDOI::linkProvisionalDoi` to register the existing + * `projects.provisional_doi` placeholder as a real findable DataCite + * record so the provisional citation keeps resolving. + * + * @return array + */ + public function createCustomDOI(string $doi, array $metadata = []): array + { + [$prefix, $suffix] = array_pad(explode('/', $doi, 2), 2, null); + + $attributes = [ + 'doi' => $doi, + 'prefix' => $prefix, + 'suffix' => $suffix, + 'publisher' => Config::get('app.name'), + 'publicationYear' => now()->format('Y'), + 'language' => 'en', + ]; + + foreach ($metadata as $key => $value) { + $attributes[$key] = $value; + } + + $body = [ + 'data' => [ + 'type' => 'dois', + 'attributes' => $attributes, + ], + ]; + + $response = $this->client->post('/dois', + [RequestOptions::JSON => $body] + ); + + $contents = $response->getBody()->getContents(); + + return json_decode($contents, true) ?? []; + } + + /** + * Fetch the `relatedIdentifiers` array currently stored on a DataCite + * record. Returns an empty array when the record has none or the + * record cannot be loaded — callers should treat the result as a + * safe baseline to merge new entries into. + * + * @return array> + */ + public function getRelatedIdentifiers(string $doi): array + { + $response = $this->client->get('/dois/'.urlencode($doi)); + $payload = json_decode($response->getBody()->getContents(), true); + + $relatedIdentifiers = $payload['data']['attributes']['relatedIdentifiers'] ?? []; + + return is_array($relatedIdentifiers) ? $relatedIdentifiers : []; + } + + /** + * PUT a fresh `relatedIdentifiers` array (and optionally an updated + * `url`) onto an existing DataCite record. Snapshots the pre-PUT + * record to `storage/logs/datacite-audit/{doi}/{timestamp}.json` + * before sending so any malformed payload can be rolled back. + * + * @param array> $relatedIdentifiers + * @return array + */ + public function putRelatedIdentifiers(string $doi, array $relatedIdentifiers, ?string $url = null): array + { + $this->snapshotForAudit($doi); + + $attributes = ['relatedIdentifiers' => $relatedIdentifiers]; + if ($url !== null && $url !== '') { + $attributes['url'] = $url; + } + + $body = [ + 'data' => [ + 'type' => 'dois', + 'attributes' => $attributes, + ], + ]; + + $response = $this->client->put('/dois/'.urlencode($doi), + [RequestOptions::JSON => $body] + ); + + $contents = $response->getBody()->getContents(); + + return json_decode($contents, true) ?? []; + } + + /** + * Best-effort GET-and-stash of a record's current state. Failures are + * logged and swallowed — losing an audit snapshot must not stop a + * legitimate metadata PUT from succeeding. + */ + private function snapshotForAudit(string $doi): void + { + try { + $response = $this->client->get('/dois/'.urlencode($doi)); + $body = $response->getBody()->getContents(); + + $disk = Storage::disk('local'); + $path = 'datacite-audit/'.str_replace('/', '_', $doi).'/'.now()->format('Ymd_His_u').'.json'; + $disk->put($path, $body); + } catch (\Throwable $e) { + Log::warning('DataCite audit snapshot failed', [ + 'doi' => $doi, + 'error' => $e->getMessage(), + ]); + } + } + /** * Update DataCite metadata based on DOI * @@ -79,6 +208,7 @@ public function createDOI($suffix, $metadata = []) */ public function updateDOI($doi, $metadata = []) { + $attributes = []; foreach ($metadata as $key => $value) { $attributes[$key] = $value; } diff --git a/app/Services/ELN/NOBSMetadataService.php b/app/Services/ELN/NOBSMetadataService.php new file mode 100644 index 000000000..ef7fe5737 --- /dev/null +++ b/app/Services/ELN/NOBSMetadataService.php @@ -0,0 +1,460 @@ +id], + ])->first(); + + if (! $publicationMetadataFile) { + Log::warning('Publication metadata file not found', [ + 'draft_id' => $draft->id, + ]); + + return null; + } + + $publicationMetadataContents = $this->fileIntegrityService->downloadFileFromStorage($publicationMetadataFile); + + if ($publicationMetadataContents === null) { + Log::warning('Could not download publication metadata file', [ + 'file_id' => $publicationMetadataFile->id, + 'path' => $publicationMetadataFile->path, + 'draft_id' => $draft->id, + ]); + + return null; + } + + $decodedMetadata = json_decode($publicationMetadataContents, true); + + if (! $decodedMetadata || ! is_array($decodedMetadata)) { + Log::warning('Invalid publication metadata JSON', [ + 'draft_id' => $draft->id, + 'file_id' => $publicationMetadataFile->id, + ]); + + return null; + } + + return $decodedMetadata; + } + + /** + * Extract project information (root level). + */ + public function extractProject(array $metadata): array + { + return [ + 'id' => $metadata['@id'] ?? null, + 'name' => $metadata['name'] ?? null, + 'description' => $metadata['description'] ?? null, + 'tracking_item_name' => $metadata['trackingItemName'] ?? null, + 'url' => $metadata['url'] ?? null, + 'license' => $metadata['license'] ?? null, + 'date_created' => $metadata['dateCreated'] ?? null, + 'date_modified' => $metadata['dateModified'] ?? null, + 'date_published' => $metadata['datePublished'] ?? null, + 'keywords' => $this->extractKeywords($metadata), + 'authors' => $this->extractAuthors($metadata), + 'publisher' => [ + 'name' => $metadata['publisher']['name'] ?? null, + 'logo' => $metadata['publisher']['logo'] ?? null, + 'url' => $metadata['publisher']['url'] ?? null, + ], + 'citation' => $metadata['citation'] ?? [], + ]; + } + + /** + * Extract studies from hasPart (can be single object or array). + */ + public function extractStudies(array $metadata): array + { + $studies = []; + $hasPart = $metadata['hasPart'] ?? null; + + if (! $hasPart) { + return $studies; + } + + // Handle both single object and array cases + $studyItems = isset($hasPart['@type']) ? [$hasPart] : $hasPart; + + if (! is_array($studyItems)) { + return $studies; + } + + foreach ($studyItems as $item) { + if (isset($item['@type']) && $item['@type'] === 'Study') { + $studies[] = [ + 'id' => $item['@id'] ?? null, + 'name' => $item['name'] ?? null, + 'tracking_item_name' => $item['trackingItemName'] ?? null, + 'description' => $item['description'] ?? null, + 'url' => $item['url'] ?? null, + 'license' => $item['license'] ?? null, + 'date_created' => $item['dateCreated'] ?? null, + 'date_modified' => $item['dateModified'] ?? null, + 'date_published' => $item['datePublished'] ?? null, + 'keywords' => $this->extractKeywords($item), + 'authors' => $this->extractAuthors($item), + 'citation' => $item['citation'] ?? [], + 'chemical_substance' => $this->extractChemicalSubstance($item), + ]; + } + } + + return $studies; + } + + /** + * Extract chemical substance from study's "about" section. + */ + private function extractChemicalSubstance(array $study): ?array + { + if (! isset($study['about']) || $study['about']['@type'] !== 'ChemicalSubstance') { + return null; + } + + $substance = $study['about']; + + return [ + 'id' => $substance['@id'] ?? null, + 'name' => $substance['name'] ?? null, + 'description' => $substance['description'] ?? null, + 'url' => $substance['url'] ?? null, + 'study_domain' => $substance['studyDomain'] ?? null, + 'study_subject' => $substance['studySubject'] ?? null, + 'molecule' => $this->extractMolecule($substance), + 'datasets' => $this->extractDatasets($substance), + ]; + } + + /** + * Extract molecule information from hasBioChemEntityPart. + */ + private function extractMolecule(array $substance): ?array + { + if (! isset($substance['hasBioChemEntityPart'])) { + return null; + } + + $molecule = $substance['hasBioChemEntityPart']; + + return [ + 'id' => $molecule['@id'] ?? null, + 'name' => $molecule['name'] ?? null, + 'molecular_formula' => $molecule['molecularFormula'] ?? null, + 'molecular_weight' => $molecule['molecularWeight']['value'] ?? null, + 'molecular_weight_unit' => $molecule['molecularWeight']['unitCode'] ?? null, + 'inchi' => $molecule['inChI'] ?? null, + 'inchi_key' => $molecule['inChIKey'] ?? null, + 'smiles' => $molecule['smiles'] ?? null, + 'iupac_name' => $molecule['iupacName'] ?? null, + ]; + } + + /** + * Extract datasets from chemical substance's hasPart (can be single object or array). + */ + private function extractDatasets(array $substance): array + { + $datasets = []; + $hasPart = $substance['hasPart'] ?? null; + + if (! $hasPart) { + return $datasets; + } + + // Handle both single object and array cases + $datasetItems = isset($hasPart['@type']) ? [$hasPart] : $hasPart; + + if (! is_array($datasetItems)) { + return $datasets; + } + + foreach ($datasetItems as $item) { + if (isset($item['@type']) && $item['@type'] === 'Dataset') { + $datasets[] = [ + 'id' => $item['@id'] ?? null, + 'name' => $item['name'] ?? null, + 'description' => $item['description'] ?? null, + 'url' => $item['url'] ?? null, + 'license' => $item['license'] ?? null, + 'date_created' => $item['dateCreated'] ?? null, + 'date_modified' => $item['dateModified'] ?? null, + 'date_published' => $item['datePublished'] ?? null, + 'analyses' => $item['analyses'] ?? null, + 'datasets' => $item['datasets'] ?? [], + 'measurement_technique' => $this->extractMeasurementTechnique($item), + 'variable_measured' => $this->extractVariableMeasured($item), + 'is_accessible_for_free' => $item['isAccessibleForFree'] ?? null, + ]; + } + } + + return $datasets; + } + + /** + * Extract analyses information. + */ + public function extractAnalyses(array $metadata): array + { + $analyses = []; + $studies = $this->extractStudies($metadata); + + foreach ($studies as $study) { + if (isset($study['chemical_substance']['datasets'])) { + foreach ($study['chemical_substance']['datasets'] as $dataset) { + if (isset($dataset['analyses'])) { + $analyses[] = [ + 'study_id' => $study['id'], + 'study_name' => $study['name'], + 'dataset_id' => $dataset['id'], + 'dataset_name' => $dataset['name'], + 'analysis_id' => $dataset['analyses'], + 'datasets' => $dataset['datasets'] ?? [], + 'measurement_technique' => $dataset['measurement_technique'], + 'variable_measured' => $dataset['variable_measured'], + 'external_url' => $dataset['url'], + ]; + } + } + } + } + + return $analyses; + } + + /** + * Extract molecules from all studies. + */ + public function extractMolecules(array $metadata): array + { + $molecules = []; + $studies = $this->extractStudies($metadata); + + foreach ($studies as $study) { + if (isset($study['chemical_substance']['molecule'])) { + $molecule = $study['chemical_substance']['molecule']; + $molecule['study_name'] = $study['name']; + $molecule['substance_name'] = $study['chemical_substance']['name']; + $molecules[] = $molecule; + } + } + + return $molecules; + } + + /** + * Extract all metadata in a structured format. + */ + public function extractAllMetadata(array $metadata): array + { + return [ + 'eln_type' => $this->getELNType(), + 'project' => $this->extractProject($metadata), + 'studies' => $this->extractStudies($metadata), + 'analyses' => $this->extractAnalyses($metadata), + 'molecules' => $this->extractMolecules($metadata), + ]; + } + + /** + * Extract authors from metadata. + */ + private function extractAuthors(array $metadata): array + { + $authors = []; + if (isset($metadata['author']) && is_array($metadata['author'])) { + foreach ($metadata['author'] as $author) { + $authors[] = [ + 'name' => $author['name'] ?? null, + 'given_name' => $author['givenName'] ?? null, + 'family_name' => $author['familyName'] ?? null, + 'identifier' => $author['identifier'] ?? null, + 'affiliation' => $author['affiliation']['name'] ?? null, + ]; + } + } + + return $authors; + } + + /** + * Extract keywords information. + */ + private function extractKeywords(array $metadata): array + { + $keywords = []; + + if (isset($metadata['keywords']) && is_array($metadata['keywords'])) { + foreach ($metadata['keywords'] as $keyword) { + $keywords[] = [ + 'name' => $keyword['name'] ?? null, + 'id' => $keyword['@id'] ?? null, + 'alternate_name' => $keyword['alternateName'] ?? null, + 'defined_term_set' => [ + 'name' => $keyword['inDefinedTermSet']['name'] ?? null, + 'id' => $keyword['inDefinedTermSet']['@id'] ?? null, + ], + ]; + } + } + + return $keywords; + } + + /** + * Extract measurement technique from dataset. + */ + private function extractMeasurementTechnique(array $dataset): ?array + { + if (! isset($dataset['measurementTechnique'])) { + return null; + } + + $technique = $dataset['measurementTechnique']; + + return [ + 'name' => $technique['name'] ?? null, + 'term_code' => $technique['termCode'] ?? null, + 'id' => $technique['@id'] ?? null, + 'alternate_names' => $technique['alternateName'] ?? [], + 'url' => $technique['url'] ?? null, + 'defined_term_set' => [ + 'name' => $technique['inDefinedTermSet']['name'] ?? null, + 'id' => $technique['inDefinedTermSet']['@id'] ?? null, + ], + ]; + } + + /** + * Extract variable measurements from dataset. + */ + private function extractVariableMeasured(array $dataset): array + { + $measurements = []; + + if (isset($dataset['variableMeasured']) && is_array($dataset['variableMeasured'])) { + foreach ($dataset['variableMeasured'] as $variable) { + $measurements[] = [ + 'name' => $variable['name'] ?? null, + 'property_id' => $variable['propertyID'] ?? null, + 'value' => $variable['value'] ?? null, + ]; + } + } + + return $measurements; + } + + /** + * Validate the NOBS metadata structure. + */ + public function validateMetadata(array $metadata): bool + { + // Basic validation for NOBS JSON-LD structure + $requiredFields = ['@context', '@type', 'name', 'hasPart']; + + foreach ($requiredFields as $field) { + if (! isset($metadata[$field])) { + Log::warning("Missing required field in NOBS metadata: {$field}"); + + return false; + } + } + + // Validate that it's a Study type + if ($metadata['@type'] !== 'Study') { + Log::warning("Invalid @type in NOBS metadata. Expected 'Study', got: ".($metadata['@type'] ?? 'null')); + + return false; + } + + // Validate schema.org context + if ($metadata['@context'] !== 'https://schema.org') { + Log::warning("Invalid @context in NOBS metadata. Expected 'https://schema.org'"); + + return false; + } + + return true; + } + + /** + * Get the ELN type this extractor handles. + */ + public function getELNType(): string + { + return 'nobs'; + } + + /** + * Extract analyses information from draft (fetches metadata internally). + */ + public function extractAnalysesFromDraft(Draft $draft): array + { + $metadata = $this->fetchPublicationMetadata($draft); + + if (! $metadata) { + return []; + } + + return $this->extractAnalyses($metadata); + } + + /** + * Validate metadata from draft (fetches metadata internally). + */ + public function validateMetadataFromDraft(Draft $draft): bool + { + $metadata = $this->fetchPublicationMetadata($draft); + + if (! $metadata) { + return false; + } + + return $this->validateMetadata($metadata); + } + + /** + * Extract all metadata from draft (fetches metadata internally). + */ + public function extractAllMetadataFromDraft(Draft $draft): ?array + { + $metadata = $this->fetchPublicationMetadata($draft); + + if (! $metadata) { + return null; + } + + return $this->extractAllMetadata($metadata); + } +} diff --git a/app/Services/ELNMetadataServiceFactory.php b/app/Services/ELNMetadataServiceFactory.php index 2ddc99d6a..2991e0833 100644 --- a/app/Services/ELNMetadataServiceFactory.php +++ b/app/Services/ELNMetadataServiceFactory.php @@ -4,6 +4,7 @@ use App\Services\ELN\ChemotionMetadataService; use App\Services\ELN\ELNMetadataExtractorInterface; +use App\Services\ELN\NOBSMetadataService; use InvalidArgumentException; /** @@ -21,6 +22,7 @@ public static function create(string $elnType): ELNMetadataExtractorInterface { return match (strtolower($elnType)) { 'chemotion' => new ChemotionMetadataService(app(FileIntegrityService::class)), + 'nobs' => new NOBSMetadataService(app(FileIntegrityService::class)), default => throw new InvalidArgumentException("Unsupported ELN type: {$elnType}") }; } @@ -32,7 +34,7 @@ public static function getSupportedELNTypes(): array { return [ 'chemotion', - // Add more ELN types here as they are implemented + 'nobs', ]; } diff --git a/app/Services/FileIntegrityService.php b/app/Services/FileIntegrityService.php index 5b75e8dd8..e6c04542a 100644 --- a/app/Services/FileIntegrityService.php +++ b/app/Services/FileIntegrityService.php @@ -3,6 +3,7 @@ namespace App\Services; use App\Models\FileSystemObject; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; @@ -178,7 +179,7 @@ private function verifyChecksums(FileSystemObject $fileSystemObject, string $fil /** * Get files pending integrity verification. */ - public function getFilesPendingVerification(int $limit = 100): \Illuminate\Database\Eloquent\Collection + public function getFilesPendingVerification(int $limit = 100): Collection { return FileSystemObject::where('type', 'file') ->where('integrity_status', 'pending') @@ -191,7 +192,7 @@ public function getFilesPendingVerification(int $limit = 100): \Illuminate\Datab /** * Get files with failed integrity verification. */ - public function getFilesWithFailedIntegrity(int $limit = 100): \Illuminate\Database\Eloquent\Collection + public function getFilesWithFailedIntegrity(int $limit = 100): Collection { return FileSystemObject::where('type', 'file') ->where('integrity_status', 'failed') diff --git a/app/Services/FileSystemObjectService.php b/app/Services/FileSystemObjectService.php index 65e1ae83d..ff14299c5 100644 --- a/app/Services/FileSystemObjectService.php +++ b/app/Services/FileSystemObjectService.php @@ -7,6 +7,8 @@ use App\Models\FileSystemObject; use App\Models\Project; use App\Models\Study; +use App\Observers\FileSystemObjectObserver; +use Illuminate\Database\QueryException; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; @@ -22,13 +24,14 @@ class FileSystemObjectService { public function __construct( private PathGeneratorService $pathGenerator, - private FileIntegrityService $integrityService + private FileIntegrityService $integrityService, + private FileSystemObjectObserver $fileSystemObjectObserver ) {} /** * Create file system object for draft uploads. * - * @throws \Illuminate\Database\QueryException + * @throws QueryException */ public function createDraftFileSystemObject( Draft $draft, @@ -51,7 +54,7 @@ public function createDraftFileSystemObject( /** * Create file system object for project uploads. * - * @throws \Illuminate\Database\QueryException + * @throws QueryException */ public function createProjectFileSystemObject( Project $project, @@ -211,7 +214,7 @@ private function findOrCreateDirectory( $directory = FileSystemObject::create($creationDefaults); return $directory; - } catch (\Illuminate\Database\QueryException $e) { + } catch (QueryException $e) { // Handle unique constraint violation - another process created it if ($e->getCode() === '23000' || str_contains($e->getMessage(), 'Duplicate entry')) { // Fetch the directory that was created by the other process @@ -286,7 +289,7 @@ private function createFileObject( $fileObject = FileSystemObject::create($creationDefaults); return ['fileObject' => $fileObject, 'wasRecentlyCreated' => true]; - } catch (\Illuminate\Database\QueryException $e) { + } catch (QueryException $e) { // Handle unique constraint violation - another process created it if ($e->getCode() === '23000' || str_contains($e->getMessage(), 'Duplicate entry')) { // Fetch the file that was created by the other process @@ -344,6 +347,19 @@ public function deleteFileSystemObject(FileSystemObject $fileSystemObject): arra // Get all objects to delete (for storage cleanup) $objectsToDelete = FileSystemObject::whereIn('id', $allIds)->get(); + // Run the same study-side invalidation the observer would have + // run for a per-row delete. We MUST do this before the mass + // `Builder::delete()` below — Eloquent skips model events for + // mass deletes, so the observer would otherwise never fire and + // the affected study would silently keep its stale download_url, + // has_nmrium, internal_status, NMRium rows, etc. Resolving the + // study here also requires the parent chain to still exist, + // which is only true before the bulk delete. + $this->fileSystemObjectObserver->invalidateForExternalChange( + $fileSystemObject, + 'service:bulk-delete' + ); + // Delete from storage first (before database deletion) foreach ($objectsToDelete as $obj) { if ($obj->type === 'file' && $obj->path) { diff --git a/app/Services/Socialite/NFDIAAI/Provider.php b/app/Services/Socialite/NFDIAAI/Provider.php index c6e15bb59..f0c865071 100644 --- a/app/Services/Socialite/NFDIAAI/Provider.php +++ b/app/Services/Socialite/NFDIAAI/Provider.php @@ -14,7 +14,25 @@ class Provider extends AbstractProvider protected function getAuthUrl($state) { - return $this->buildAuthUrlFromBase('https://regapp.nfdi-aai.de/oidc/realms/nfdi/protocol/openid-connect/auth', $state); + $url = $this->buildAuthUrlFromBase('https://regapp.nfdi-aai.de/oidc/realms/nfdi/protocol/openid-connect/auth', $state); + + // Ensure client_id is in the URL + if (! str_contains($url, 'client_id=')) { + $separator = str_contains($url, '?') ? '&' : '?'; + $url .= $separator.'client_id='.urlencode($this->clientId); + } + + return $url; + } + + protected function getCodeFields($state = null): array + { + $fields = parent::getCodeFields($state); + + // Ensure client_id is always included + $fields['client_id'] = $this->clientId; + + return $fields; } protected function getTokenUrl() diff --git a/app/Support/Csp/Policies/NmrxivPolicy.php b/app/Support/Csp/Policies/NmrxivPolicy.php new file mode 100644 index 000000000..a105154ba --- /dev/null +++ b/app/Support/Csp/Policies/NmrxivPolicy.php @@ -0,0 +1,136 @@ +add(Directive::BASE, Keyword::SELF) + ->add(Directive::DEFAULT, Keyword::SELF) + ->add(Directive::FORM_ACTION, Keyword::SELF) + ->add(Directive::OBJECT, Keyword::NONE); + + // Basic asset sources + $policy + ->add(Directive::SCRIPT, Keyword::SELF) + ->add(Directive::STYLE, Keyword::SELF) + ->add(Directive::FONT, 'data:') + ->add(Directive::CONNECT, Keyword::SELF); + + // Third-party services + $policy + ->add(Directive::STYLE, 'https://fonts.bunny.net') + ->add(Directive::SCRIPT, 'https://matomo.nfdi4chem.de') + ->add(Directive::CONNECT, 'https://matomo.nfdi4chem.de', 'https://fonts.bunny.net'); + + // Add nmrXiv-specific external sources + $this->addNmrxivSources($policy); + + // Unified rules for all environments + $this->addUnifiedRules($policy); + } + + private function addUnifiedRules(Policy $policy): void + { + // Allow unsafe-inline and unsafe-eval for maximum compatibility + // This is acceptable when you control all inline scripts and have other security layers + $policy + ->add(Directive::SCRIPT, Keyword::UNSAFE_INLINE) + ->add(Directive::SCRIPT, Keyword::UNSAFE_EVAL) + ->add(Directive::STYLE, Keyword::UNSAFE_INLINE); + + // Development server support (for local development with Vite) + $policy + ->add(Directive::SCRIPT, self::LOCAL_HOSTS) + ->add(Directive::STYLE, self::LOCAL_HOSTS) + ->add(Directive::CONNECT, array_merge(self::LOCAL_WS_HOSTS, self::LOCAL_HOSTS)); + } + + /** + * Add nmrXiv-specific external sources. + * For runtime-configurable sources, use config/csp.php with CSP_ADDITIONAL_* env variables. + */ + private function addNmrxivSources(Policy $policy): void + { + // Image sources + $policy + ->add(Directive::IMG, Keyword::SELF) + ->add(Directive::IMG, 'data:') + ->add(Directive::IMG, 'blob:') + ->add(Directive::IMG, 'https://s3.uni-jena.de') + ->add(Directive::IMG, 'https://orcid.org') + ->add(Directive::IMG, 'https://ui-avatars.com') + ->add(Directive::IMG, 'https://www.nfdi4chem.de') + ->add(Directive::IMG, 'https://www.nmrium.org') + ->add(Directive::IMG, 'https://nmriumdev.nmrxiv.org') + ->add(Directive::IMG, 'https://upload.wikimedia.org') + ->add(Directive::IMG, 'https://pbs.twimg.com') + ->add(Directive::IMG, 'https://api.cheminf.studio') + ->add(Directive::IMG, 'https://api.naturalproducts.net') + ->add(Directive::IMG, 'https://dev.api.naturalproducts.net') + ->add(Directive::IMG, 'https://placehold.co'); + + // Connection sources + $policy + ->add(Directive::CONNECT, config('external-links.datacite_api')) + ->add(Directive::CONNECT, config('external-links.crossref_api')) + ->add(Directive::CONNECT, config('doi.datacite.endpoint')) + ->add(Directive::CONNECT, config('external-links.nmrkit_url')) + ->add(Directive::CONNECT, config('services.pubchem.base_url')) + ->add(Directive::CONNECT, config('services.cas.base_url')) + ->add(Directive::CONNECT, config('services.chemistry_standardize.url')) + ->add(Directive::CONNECT, config('external-links.europemc_ws_api')) + ->add(Directive::CONNECT, config('services.chemotion_tracker.base_url')) + ->add(Directive::CONNECT, config('external-links.cm_api')) + ->add(Directive::CONNECT, config('filesystems.disks.ceph.endpoint')) + ->add(Directive::CONNECT, 'https://service.tib.eu') + ->add(Directive::CONNECT, 'https://nmrium.nmrxiv.org') + ->add(Directive::CONNECT, 'https://nmriumdev.nmrxiv.org') + ->add(Directive::CONNECT, 'https://api.cheminf.studio') + ->add(Directive::CONNECT, 'https://api.naturalproducts.net') + ->add(Directive::CONNECT, 'https://dev.api.naturalproducts.net'); + + // Font sources + $policy + ->add(Directive::FONT, 'https://fonts.googleapis.com') + ->add(Directive::FONT, 'https://fonts.gstatic.com') + ->add(Directive::FONT, 'https://fonts.bunny.net'); + + // Frame sources + $policy + ->add(Directive::FRAME, Keyword::SELF) + ->add(Directive::FRAME, 'https://api.cheminf.studio') + ->add(Directive::FRAME, 'https://api.naturalproducts.net') + ->add(Directive::FRAME, 'https://dev.api.naturalproducts.net') + ->add(Directive::FRAME, 'https://nmrium.nmrxiv.org') + ->add(Directive::FRAME, 'https://nmriumdev.nmrxiv.org'); + } +} diff --git a/app/Support/Dashboard/CompoundLibraryRankedStudiesQuery.php b/app/Support/Dashboard/CompoundLibraryRankedStudiesQuery.php new file mode 100644 index 000000000..3270f0a13 --- /dev/null +++ b/app/Support/Dashboard/CompoundLibraryRankedStudiesQuery.php @@ -0,0 +1,61 @@ +getDriverName(); + + $studyKeyExpr = match ($driver) { + 'pgsql', 'sqlite' => "'study-' || s.id", + default => "CONCAT('study-', s.id)", + }; + + $partitionExpr = 'COALESCE(NULLIF(TRIM(m.standard_inchi_key), \'\'), NULLIF(TRIM(m.inchi_key), \'\'), '.$studyKeyExpr.')'; + + $ranked = DB::table('studies as s') + ->leftJoin('samples as sa', 'sa.study_id', '=', 's.id') + ->leftJoinSub( + DB::table('molecule_sample') + ->select('sample_id', DB::raw('MIN(molecule_id) as molecule_id')) + ->groupBy('sample_id'), + 'ms_pick', + fn ($join) => $join->on('ms_pick.sample_id', '=', 'sa.id') + ) + ->leftJoin('molecules as m', 'm.id', '=', 'ms_pick.molecule_id') + ->where('s.is_deleted', '=', false) + ->where(function ($w): void { + $w->whereNull('s.project_id') + ->orWhereExists(function ($e): void { + $e->selectRaw('1') + ->from('projects as p') + ->whereColumn('p.id', 's.project_id') + ->where('p.is_deleted', '=', false); + }); + }) + ->where('s.team_id', '=', $team->id); + + if ($team->personal_team) { + $ranked->where('s.owner_id', '=', $user->id); + } + + $ranked->selectRaw( + 's.id, ROW_NUMBER() OVER (PARTITION BY '.$partitionExpr.' ORDER BY s.updated_at DESC) as rn' + ); + + return $ranked; + } +} diff --git a/app/Support/Dashboard/WorkspaceMoleculeAggregates.php b/app/Support/Dashboard/WorkspaceMoleculeAggregates.php new file mode 100644 index 000000000..9cc69c56b --- /dev/null +++ b/app/Support/Dashboard/WorkspaceMoleculeAggregates.php @@ -0,0 +1,93 @@ +|BelongsToMany $query + */ + public static function applyToMoleculeRelation(Builder|BelongsToMany $query, User $user, Team $team): void + { + $query->withCount([ + 'samples as workspace_samples_count' => function (Builder|BelongsToMany $sq) use ($user, $team): void { + $sq->whereHas('study', function (Builder $studyQuery) use ($user, $team): void { + self::scopeStudiesToWorkspace($studyQuery, $user, $team); + }); + }, + ]); + + $query->addSelect([ + DB::raw(self::experimentTypeCountsExpression($user, $team).' as workspace_experiment_type_counts'), + ]); + } + + /** + * @param Builder $studyQuery + */ + private static function scopeStudiesToWorkspace(Builder $studyQuery, User $user, Team $team): void + { + $studyQuery->where('team_id', $team->id) + ->where('is_deleted', false); + if ($team->personal_team) { + $studyQuery->where('owner_id', $user->id); + } + } + + private static function experimentTypeCountsExpression(User $user, Team $team): string + { + $inner = trim(preg_replace('/\s+/', ' ', self::experimentTypeCountsInnerSql($user, $team))); + + return match (DB::connection()->getDriverName()) { + 'pgsql' => "(SELECT COALESCE((SELECT json_object_agg(sub.k, sub.v) FROM ({$inner}) AS sub), '{}'::json))", + 'sqlite' => "(SELECT COALESCE((SELECT json_group_object(sub.k, sub.v) FROM ({$inner}) AS sub), '{}'))", + 'mysql', 'mariadb' => "(SELECT COALESCE((SELECT JSON_OBJECTAGG(sub.k, sub.v) FROM ({$inner}) AS sub), JSON_OBJECT()))", + default => "(SELECT '{}')", + }; + } + + /** + * Raw inner SELECT for grouping dataset types; correlated on outer {@code molecules.id}. + * Identifiers are bound via integer casts (team_id, owner_id) to avoid injection. + */ + private static function experimentTypeCountsInnerSql(User $user, Team $team): string + { + $teamId = (int) $team->id; + $ownerClause = $team->personal_team + ? ' AND st.owner_id = '.(int) $user->id + : ''; + + $datasetNotDeleted = match (DB::connection()->getDriverName()) { + 'pgsql' => '(NOT COALESCE(d.is_deleted, false))', + default => '(d.is_deleted IS NULL OR d.is_deleted = 0 OR d.is_deleted = false)', + }; + + return << '' +AND {$datasetNotDeleted} +GROUP BY d.type +SQL; + } +} diff --git a/app/Support/DataCite/MetadataEnricher.php b/app/Support/DataCite/MetadataEnricher.php new file mode 100644 index 000000000..b4b273781 --- /dev/null +++ b/app/Support/DataCite/MetadataEnricher.php @@ -0,0 +1,746 @@ +>, + * descriptions?: list>, + * sizes?: list, + * formats?: list, + * alternateIdentifiers?: list>, + * relatedIdentifiers?: list>, + * } + */ + public function forProject(Project $project): array + { + try { + $studies = $project->studies()->with(['datasets', 'sample.molecules'])->get(); + + $fragments = []; + $compoundFragments = []; + $datasetCount = 0; + $sampleCount = 0; + $formats = []; + + foreach ($studies as $study) { + $sampleCount++; + foreach ($study->datasets as $dataset) { + $datasetCount++; + $fragments[] = $this->buildDatasetFragment($dataset, includeCompounds: false); + foreach ($this->detectFormatsForDataset($dataset) as $format) { + $formats[$format] = true; + } + } + $compoundFragments[] = $this->buildCompoundFragment($study->sample); + } + + $merged = $this->mergeFragments($fragments); + $compoundMerged = $this->mergeFragments($compoundFragments); + $merged = $this->mergeFragments([$merged, $compoundMerged]); + + $merged['sizes'] = array_values(array_filter([ + $datasetCount > 0 ? "{$datasetCount} NMR datasets across {$sampleCount} samples" : null, + ])); + $merged['formats'] = array_keys($formats); + $merged['alternateIdentifiers'] = array_merge( + $merged['alternateIdentifiers'] ?? [], + $this->nmrxivIdentifierFor($project) + ); + + return $this->dedupe($merged); + } catch (Throwable $e) { + return []; + } + } + + /** + * @return array + */ + public function forStudy(Study $study): array + { + try { + $datasets = $study->datasets; + $fragments = []; + $formats = []; + + foreach ($datasets as $dataset) { + $fragments[] = $this->buildDatasetFragment($dataset, includeCompounds: false); + foreach ($this->detectFormatsForDataset($dataset) as $format) { + $formats[$format] = true; + } + } + $fragments[] = $this->buildCompoundFragment($study->sample); + + $merged = $this->mergeFragments($fragments); + $count = $datasets->count(); + $merged['sizes'] = $count > 0 ? ["{$count} NMR datasets"] : []; + $merged['formats'] = array_keys($formats); + $merged['alternateIdentifiers'] = array_merge( + $merged['alternateIdentifiers'] ?? [], + $this->nmrxivIdentifierFor($study) + ); + + return $this->dedupe($merged); + } catch (Throwable $e) { + return []; + } + } + + /** + * @return array + */ + public function forDataset(Dataset $dataset): array + { + try { + $fragment = $this->buildDatasetFragment($dataset, includeCompounds: true); + $fragment['formats'] = $this->detectFormatsForDataset($dataset); + $fragment['alternateIdentifiers'] = array_merge( + $fragment['alternateIdentifiers'] ?? [], + $this->nmrxivIdentifierFor($dataset) + ); + + return $this->dedupe($fragment); + } catch (Throwable $e) { + return []; + } + } + + // -------------------------------------------------------------------- // + // Fragment building // + // -------------------------------------------------------------------- // + + /** + * Build the MIChI fragment for a single dataset, optionally including + * the parent sample's characterized compounds (Dataset-level emission; + * Study/Project levels merge compounds separately to avoid duplicates). + * + * @return array + */ + private function buildDatasetFragment(Dataset $dataset, bool $includeCompounds): array + { + $info = $this->safeGetNmriumInfo($dataset); + + $sample = $dataset->study?->sample; + + $subjects = []; + $methodLines = []; + $sizes = []; + + $nucleus = null; + $dimension = null; + + foreach (MichiV1Schema::rows() as $row) { + $extractor = $row['extractor']; + if (! method_exists($this, $extractor)) { + continue; + } + $value = $this->{$extractor}($info, $sample, $dataset); + if ($value === null || $value === [] || $value === '') { + continue; + } + + $values = is_array($value) ? $value : [$value]; + + foreach ($values as $entry) { + if ($row['kind'] === 'subject') { + $subjects[] = $this->buildSubjectEntry($row, $entry); + } elseif ($row['kind'] === 'numeric') { + $methodLines[] = $this->formatNumericLine($row, $entry); + $subjects[] = $this->buildSubjectEntry($row, (string) $entry['display'] ?? (string) $entry); + } + } + + if ($row['id'] === 'nfdi.nmr.acquisition.nucleus') { + $nucleus = is_array($value) ? ($value[0]['display'] ?? null) : null; + } + } + + if (is_object($info) && property_exists($info, 'dimension') && $info->dimension !== null) { + $dimension = (int) $info->dimension; + } + + if ($dimension !== null && $nucleus !== null) { + $sizes[] = "{$dimension}D {$nucleus} spectrum"; + } + + $compoundFragment = $includeCompounds + ? $this->buildCompoundFragment($sample) + : ['subjects' => [], 'descriptions' => [], 'alternateIdentifiers' => [], 'relatedIdentifiers' => []]; + + $descriptions = []; + if (! empty($methodLines)) { + $descriptions[] = [ + 'description' => "MIChI v1 NMR acquisition parameters:\n- ".implode("\n- ", $methodLines), + 'descriptionType' => 'Methods', + 'lang' => 'en', + ]; + } + + return $this->mergeFragments([ + [ + 'subjects' => $subjects, + 'descriptions' => $descriptions, + 'sizes' => $sizes, + 'alternateIdentifiers' => [], + 'relatedIdentifiers' => [], + ], + $compoundFragment, + ]); + } + + /** + * Build a fragment carrying MIChI 1.1.1 (Characterized Compound) for the + * given sample's molecules. Emitted at every level (Dataset / Study / + * Project) since the dataset IS the NMR data of this sample. + * + * @return array + */ + private function buildCompoundFragment(?Sample $sample): array + { + if ($sample === null) { + return ['subjects' => [], 'descriptions' => [], 'alternateIdentifiers' => [], 'relatedIdentifiers' => []]; + } + + $subjects = []; + $alternates = []; + $related = []; + $compoundLines = []; + + $molecules = $sample->molecules()->get(); + + foreach ($molecules as $molecule) { + $label = $molecule->iupac_name + ?? $molecule->name + ?? $molecule->canonical_smiles + ?? $molecule->smiles + ?? null; + + if ($label !== null && $label !== '') { + $subjects[] = [ + 'subject' => (string) $label, + 'subjectScheme' => 'IUPAC', + 'classificationCode' => 'nfdi.nmr.sample.compound', + ]; + } + + if (! empty($molecule->molecular_formula)) { + $subjects[] = [ + 'subject' => (string) $molecule->molecular_formula, + 'subjectScheme' => 'molecularFormula', + 'classificationCode' => 'nfdi.nmr.sample.compound', + ]; + } + + $inchiKey = $molecule->inchi_key ?? $molecule->standard_inchi_key ?? null; + if (! empty($inchiKey)) { + $alternates[] = [ + 'alternateIdentifier' => (string) $inchiKey, + 'alternateIdentifierType' => 'InChIKey', + ]; + } + + $inchi = $molecule->standard_inchi ?? $molecule->inchi ?? null; + + $normalizedMoleculeDoi = $this->normalizeBareDoi($molecule->doi ?? null); + if ($normalizedMoleculeDoi !== null) { + $related[] = [ + 'relatedIdentifier' => $normalizedMoleculeDoi, + 'relatedIdentifierType' => 'DOI', + 'relationType' => 'IsRelatedTo', + ]; + } + + $landingPage = $this->moleculeLandingPage($molecule); + if ($landingPage !== null) { + $related[] = [ + 'relatedIdentifier' => $landingPage, + 'relatedIdentifierType' => 'URL', + 'relationType' => 'IsDescribedBy', + ]; + } + + $percentage = $molecule->pivot->percentage_composition ?? null; + $compoundDescriptor = (string) ($label ?? $inchiKey ?? $molecule->id); + if (! empty($inchiKey)) { + $compoundDescriptor .= " [InChIKey={$inchiKey}"; + if ($percentage !== null && $percentage !== '') { + $compoundDescriptor .= ", {$percentage}%"; + } + $compoundDescriptor .= ']'; + } elseif ($percentage !== null && $percentage !== '') { + $compoundDescriptor .= " [{$percentage}%]"; + } + // InChI and CAS are not valid DataCite relatedIdentifierType values; + // emit them in the Methods block instead so compound info stays public. + if (! empty($inchi)) { + $compoundDescriptor .= '; InChI='.(string) $inchi; + } + if (! empty($molecule->cas)) { + $compoundDescriptor .= '; CAS='.(string) $molecule->cas; + } + $compoundLines[] = $compoundDescriptor; + } + + $descriptions = []; + if (! empty($compoundLines)) { + $descriptions[] = [ + 'description' => 'Characterized compounds (nfdi.nmr.sample.compound): '.implode('; ', $compoundLines), + 'descriptionType' => 'Methods', + 'lang' => 'en', + ]; + } + + return [ + 'subjects' => $subjects, + 'descriptions' => $descriptions, + 'alternateIdentifiers' => $alternates, + 'relatedIdentifiers' => $related, + ]; + } + + /** + * Build a single DataCite `subjects[]` entry for a MIChI row. + * + * @param array $row + * @param mixed $value Either a scalar (subject text) or array{display: string, ...}. + * @return array + */ + private function buildSubjectEntry(array $row, $value): array + { + $display = is_array($value) ? ($value['display'] ?? null) : (string) $value; + $valueUri = is_array($value) ? ($value['valueUri'] ?? null) : null; + $valueUri = $valueUri ?: $row['ontologyIri'] ?? null; + + $entry = [ + 'subject' => (string) $display, + 'classificationCode' => $row['id'], + ]; + + if (! empty($row['subjectScheme']) && $row['subjectScheme'] !== 'TEXT') { + $entry['subjectScheme'] = $row['subjectScheme']; + } + + if (! empty($valueUri)) { + $entry['valueURI'] = $valueUri; + } + + return $entry; + } + + /** + * @param array $row + * @param mixed $entry + */ + private function formatNumericLine(array $row, $entry): string + { + $display = is_array($entry) ? ($entry['display'] ?? '') : (string) $entry; + $unit = $row['unitDisplay'] ?? ''; + + return sprintf('%s (%s): %s%s', $row['label'], $row['id'], $display, $unit ? ' '.$unit : ''); + } + + // -------------------------------------------------------------------- // + // Extractors invoked by name from the schema rows // + // -------------------------------------------------------------------- // + + /** + * @param mixed $info + * @return array|null + */ + private function solventFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $solvent = $this->property($info, 'solvent'); + if ($solvent === null || $solvent === '') { + return null; + } + $solventStr = (string) $solvent; + + return [[ + 'display' => $solventStr, + 'valueUri' => SolventChebiMap::lookup($solventStr), + ]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function nucleusFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $nucleus = $this->property($info, 'nucleus'); + if ($nucleus === null) { + return null; + } + + $nuclei = is_array($nucleus) ? $nucleus : [$nucleus]; + $out = []; + foreach ($nuclei as $n) { + if ($n === null || $n === '') { + continue; + } + $out[] = [ + 'display' => (string) $n, + 'valueUri' => null, + ]; + } + + return empty($out) ? null : $out; + } + + /** + * @param mixed $info + * @return array|null + */ + private function methodFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $experiment = $this->property($info, 'experiment'); + if (empty($experiment)) { + return null; + } + + return [[ + 'display' => (string) $experiment, + 'valueUri' => null, + ]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function pulseSequenceFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $pulse = $this->property($info, 'pulseSequence'); + if (empty($pulse)) { + return null; + } + + return [[ + 'display' => (string) $pulse, + 'valueUri' => null, + ]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function baseFrequencyFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $value = $this->property($info, 'baseFrequency'); + if ($value === null || $value === '') { + return null; + } + + return [['display' => (string) $value]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function temperatureFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $value = $this->property($info, 'temperature'); + if ($value === null || $value === '') { + return null; + } + + return [['display' => (string) $value]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function relaxationDelayFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $value = $this->property($info, 'relaxationTime'); + if ($value === null || $value === '') { + return null; + } + + return [['display' => (string) $value]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function numberOfPointsFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $value = $this->property($info, 'numberOfPoints'); + if ($value === null || $value === '') { + return null; + } + + return [['display' => (string) $value]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function numberOfScansFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $value = $this->property($info, 'numberOfScans'); + if ($value === null || $value === '') { + return null; + } + + return [['display' => (string) $value]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function spectralWidthFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $value = $this->property($info, 'spectralWidth'); + if ($value === null || $value === '') { + return null; + } + + return [['display' => (string) $value]]; + } + + /** + * @param mixed $info + * @return array|null + */ + private function probeFromNmriumInfo($info, ?Sample $sample, Dataset $dataset): ?array + { + $value = $this->property($info, 'probeName'); + if (empty($value)) { + return null; + } + + return [[ + 'display' => (string) $value, + 'valueUri' => null, + ]]; + } + + /** + * Compounds extractor referenced by the schema row. The actual + * compound subject emission is handled by `buildCompoundFragment` + * because it produces multiple DataCite fragment keys at once; + * returning null here keeps the per-row loop a no-op. + * + * @param mixed $info + */ + private function compoundsFromSample($info, ?Sample $sample, Dataset $dataset): ?array + { + return null; + } + + // -------------------------------------------------------------------- // + // Helpers // + // -------------------------------------------------------------------- // + + /** + * Return a bare DOI suitable for DataCite's relatedIdentifierType "DOI" + * (e.g. `10.x/foo`), stripping resolver prefixes when present. + */ + private function normalizeBareDoi(?string $doi): ?string + { + if ($doi === null || trim($doi) === '') { + return null; + } + + $doi = trim($doi); + $lower = strtolower($doi); + foreach (['https://doi.org/', 'http://doi.org/', 'doi:'] as $prefix) { + if (str_starts_with($lower, strtolower($prefix))) { + return substr($doi, strlen($prefix)); + } + } + + return $doi; + } + + /** + * Read a property defensively from `nmrium_info` whether it arrived as + * an `stdClass`, an array, or null. + * + * @param mixed $info + * @return mixed + */ + private function property($info, string $key) + { + if ($info === null) { + return null; + } + if (is_array($info)) { + return $info[$key] ?? null; + } + if (is_object($info) && property_exists($info, $key)) { + return $info->{$key}; + } + + return null; + } + + /** + * @return mixed + */ + private function safeGetNmriumInfo(Dataset $dataset) + { + try { + return BioschemasHelper::getNMRiumInfo($dataset); + } catch (Throwable $e) { + return null; + } + } + + /** + * @return list + */ + private function detectFormatsForDataset(Dataset $dataset): array + { + $type = $dataset->type ?? ''; + $type = is_string($type) ? strtolower($type) : ''; + + $formats = []; + if (str_contains($type, 'bruker')) { + $formats[] = 'chemical/x-bruker'; + } + if (str_contains($type, 'jcamp') || str_contains($type, 'jdx') || str_contains($type, 'dx')) { + $formats[] = 'chemical/x-jcamp-dx'; + } + if (str_contains($type, 'varian')) { + $formats[] = 'chemical/x-varian'; + } + if (empty($formats)) { + $formats[] = 'application/zip'; + } + + return $formats; + } + + /** + * @return list> + */ + private function nmrxivIdentifierFor($model): array + { + $raw = $model->getRawOriginal('identifier') ?? null; + if (empty($raw)) { + return []; + } + + $prefix = match (true) { + $model instanceof Project => 'P', + $model instanceof Study => 'S', + $model instanceof Dataset => 'D', + default => null, + }; + if ($prefix === null) { + return []; + } + + return [[ + 'alternateIdentifier' => $prefix.$raw, + 'alternateIdentifierType' => 'NMRXIV', + ]]; + } + + private function moleculeLandingPage(Molecule $molecule): ?string + { + $raw = $molecule->getRawOriginal('identifier') ?? null; + if (empty($raw)) { + return null; + } + + $appUrl = rtrim((string) config('app.url'), '/'); + if ($appUrl === '') { + return null; + } + + return $appUrl.'/compound/M'.$raw; + } + + /** + * Merge a list of fragments into one. Distinct from `array_merge_recursive` + * because we want simple list concatenation per top-level key. + * + * @param list> $fragments + * @return array + */ + private function mergeFragments(array $fragments): array + { + $merged = [ + 'subjects' => [], + 'descriptions' => [], + 'sizes' => [], + 'formats' => [], + 'alternateIdentifiers' => [], + 'relatedIdentifiers' => [], + ]; + + foreach ($fragments as $fragment) { + foreach (array_keys($merged) as $key) { + if (! empty($fragment[$key]) && is_array($fragment[$key])) { + $merged[$key] = array_merge($merged[$key], $fragment[$key]); + } + } + } + + return $merged; + } + + /** + * De-duplicate list entries inside a fragment by their JSON-encoded + * shape. Cheap, stable, and correct for the small lists we emit. + * + * @param array $fragment + * @return array + */ + private function dedupe(array $fragment): array + { + foreach ($fragment as $key => $list) { + if (! is_array($list)) { + continue; + } + $seen = []; + $out = []; + foreach ($list as $entry) { + $hash = is_array($entry) ? json_encode($entry) : (string) $entry; + if (isset($seen[$hash])) { + continue; + } + $seen[$hash] = true; + $out[] = $entry; + } + $fragment[$key] = $out; + } + + return $fragment; + } +} diff --git a/app/Support/Nmr/JcampDatasetClassifier.php b/app/Support/Nmr/JcampDatasetClassifier.php new file mode 100644 index 000000000..6e87d9f5d --- /dev/null +++ b/app/Support/Nmr/JcampDatasetClassifier.php @@ -0,0 +1,164 @@ + label] pairs that were + * updated so callers can log/audit. + * + * @return array + */ + public function classifyStudy(Study $study): array + { + $candidates = $this->candidates($study); + if ($candidates->isEmpty()) { + return []; + } + + $url = $study->download_url; + if (! $url) { + return []; + } + + $tmp = tempnam(sys_get_temp_dir(), 'jcamp-zip-'); + if ($tmp === false) { + return []; + } + + try { + $bytes = @file_get_contents($url); + if ($bytes === false || $bytes === '') { + return []; + } + file_put_contents($tmp, $bytes); + + $zip = new ZipArchive; + if ($zip->open($tmp) !== true) { + return []; + } + + $rootName = $study->fsObject?->name; + $updates = []; + + foreach ($candidates as $dataset) { + $fso = $dataset->fsObject; + if (! $fso) { + continue; + } + $entry = $this->locateZipEntry($zip, $fso->relative_url, $rootName, $fso->name); + if ($entry === null) { + continue; + } + $content = $zip->getFromName($entry); + if ($content === false || $content === '') { + continue; + } + $headers = JcampHeaderReader::parseHeaders($content); + if ($headers === null || empty($headers['nucleus'])) { + continue; + } + $label = $this->labelFromHeaders($headers); + if ($label === null || $label === $dataset->type) { + continue; + } + $dataset->type = $label; + $dataset->save(); + $updates[$dataset->id] = $label; + } + $zip->close(); + + return $updates; + } finally { + @unlink($tmp); + } + } + + /** + * @return Collection + */ + protected function candidates(Study $study): Collection + { + return $study->datasets->filter(function ($dataset) { + $fso = $dataset->fsObject; + if (! $fso || $fso->type !== 'file') { + return false; + } + $name = strtolower((string) $fso->name); + if (! preg_match('/\.(jdx|dx|jcamp)$/', $name)) { + return false; + } + + return empty($dataset->type); + })->values(); + } + + /** + * @param array{nucleus: ?string, experiment: ?string, dimension: ?int, dataType: ?string} $headers + */ + protected function labelFromHeaders(array $headers): ?string + { + $nucleus = $headers['nucleus'] ?? null; + if ($nucleus === null) { + return null; + } + + $suffix = match ($headers['dataType'] ?? null) { + 'NMR SPECTRUM', 'NMR FID' => ($headers['dimension'] ?? 1).'D', + 'NMR PEAK TABLE' => 'Peak Table', + 'NMR PEAK ASSIGNMENTS' => 'Peak Assignments', + default => null, + }; + + return $suffix !== null ? $nucleus.' NMR - '.$suffix : $nucleus.' NMR'; + } + + protected function locateZipEntry(ZipArchive $zip, ?string $relativeUrl, ?string $rootName, string $fallbackName): ?string + { + $candidates = []; + if ($relativeUrl) { + $trim = ltrim($relativeUrl, '/'); + $candidates[] = $trim; + if ($rootName && ! str_starts_with($trim, $rootName.'/')) { + $candidates[] = $rootName.'/'.$trim; + } + } + if ($rootName) { + $candidates[] = $rootName.'/'.$fallbackName; + } + + foreach ($candidates as $candidate) { + if ($zip->locateName($candidate) !== false) { + return $candidate; + } + } + + for ($i = 0; $i < $zip->numFiles; $i++) { + $name = $zip->getNameIndex($i); + if ($name !== false && basename($name) === $fallbackName) { + return $name; + } + } + + return null; + } +} diff --git a/app/Support/Nmr/JcampHeaderReader.php b/app/Support/Nmr/JcampHeaderReader.php new file mode 100644 index 000000000..802544516 --- /dev/null +++ b/app/Support/Nmr/JcampHeaderReader.php @@ -0,0 +1,152 @@ + self::normaliseNucleus($chosen['.OBSERVE NUCLEUS'] ?? null), + 'experiment' => self::deriveExperiment($dataType), + 'dimension' => self::guessDimension($chosen), + 'dataType' => $dataType, + ]; + } + + /** + * Convert JCAMP nucleus encodings such as `^1H`, `<1H>`, `1H` to a + * canonical `1H` / `13C` / etc. + */ + protected static function normaliseNucleus(?string $raw): ?string + { + if ($raw === null) { + return null; + } + $value = trim($raw, "<>\t \"^"); + $value = preg_replace('/^\^/', '', $value) ?? $value; + + return $value !== '' ? $value : null; + } + + /** + * Map JCAMP `DATA TYPE` to the NMRium-style experiment token used by + * `spectrumTypeLabel`. Anything that is not a recognised NMR datatype + * returns `null` so the caller can decide on a fallback. + */ + protected static function deriveExperiment(?string $dataType): ?string + { + if ($dataType === null) { + return null; + } + + return match ($dataType) { + 'NMR SPECTRUM' => '1d', + 'NMR FID' => '1d', + 'NMR PEAK TABLE' => 'peak-table', + 'NMR PEAK ASSIGNMENTS' => 'peak-assignments', + default => null, + }; + } + + /** + * JCAMP-DX 6.0 stores 2D spectra as nTUPLES blocks; we only see those + * here as the 1D `NUM DIM=2` LDR. Treat anything else as 1D. + */ + protected static function guessDimension(array $block): ?int + { + $numDim = $block['NUM DIM'] ?? $block['NUMDIM'] ?? null; + if (is_numeric($numDim)) { + return (int) $numDim; + } + + return 1; + } +} diff --git a/app/Support/Nmr/MichiV1Schema.php b/app/Support/Nmr/MichiV1Schema.php new file mode 100644 index 000000000..e23398386 --- /dev/null +++ b/app/Support/Nmr/MichiV1Schema.php @@ -0,0 +1,188 @@ +> + */ + public static function rows(): array + { + return [ + // 1.1 NMR Sample + [ + 'id' => 'nfdi.nmr.sample.compound', + 'label' => 'Characterized Compound', + 'level' => 1, + 'cardinality' => '1-n', + 'ontologyIri' => 'http://purl.obolibrary.org/obo/CHEBI_23367', + 'subjectScheme' => 'CHEBI', + 'unitIri' => null, + 'unitDisplay' => null, + 'extractor' => 'compoundsFromSample', + 'kind' => 'subject', + ], + [ + 'id' => 'nfdi.nmr.sample.solvent', + 'label' => 'NMR Solvent', + 'level' => 1, + 'cardinality' => '1-n', + 'ontologyIri' => 'http://purl.obolibrary.org/obo/CHEBI_197449', + 'subjectScheme' => 'CHEBI', + 'unitIri' => null, + 'unitDisplay' => null, + 'extractor' => 'solventFromNmriumInfo', + 'kind' => 'subject', + ], + + // 1.2 NMR Acquisition Parameters + [ + 'id' => 'nfdi.nmr.acquisition.nucleus', + 'label' => 'Acquisition Nucleus', + 'level' => 1, + 'cardinality' => '1-d', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1400083', + 'subjectScheme' => 'NMRCV', + 'unitIri' => null, + 'unitDisplay' => null, + 'extractor' => 'nucleusFromNmriumInfo', + 'kind' => 'subject', + ], + [ + 'id' => 'nfdi.nmr.acquisition.proton_frequency', + 'label' => 'Nominal Proton Frequency', + 'level' => 1, + 'cardinality' => '1-d', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1400026', + 'subjectScheme' => 'NMRCV', + 'unitIri' => 'http://purl.obolibrary.org/obo/UO_0000325', + 'unitDisplay' => 'MHz', + 'extractor' => 'baseFrequencyFromNmriumInfo', + 'kind' => 'numeric', + ], + [ + 'id' => 'nfdi.nmr.acquisition.method', + 'label' => 'NMR Method', + 'level' => 1, + 'cardinality' => '1', + 'ontologyIri' => 'http://purl.obolibrary.org/obo/CHMO_0000613', + 'subjectScheme' => 'CHMO', + 'unitIri' => null, + 'unitDisplay' => null, + 'extractor' => 'methodFromNmriumInfo', + 'kind' => 'subject', + ], + [ + 'id' => 'nfdi.nmr.acquisition.pulse', + 'label' => 'Pulse Sequence Name', + 'level' => 1, + 'cardinality' => '1', + 'ontologyIri' => null, + 'subjectScheme' => 'TEXT', + 'unitIri' => null, + 'unitDisplay' => null, + 'extractor' => 'pulseSequenceFromNmriumInfo', + 'kind' => 'subject', + ], + [ + 'id' => 'nfdi.nmr.acquisition.relaxation_delay', + 'label' => 'Relaxation Delay', + 'level' => 2, + 'cardinality' => '0-1', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1400090', + 'subjectScheme' => 'NMRCV', + 'unitIri' => 'http://purl.obolibrary.org/obo/UO_0000010', + 'unitDisplay' => 's', + 'extractor' => 'relaxationDelayFromNmriumInfo', + 'kind' => 'numeric', + ], + [ + 'id' => 'nfdi.nmr.acquisition.number_of_acquisition_data_points', + 'label' => 'Number of Acquisition Data Points', + 'level' => 2, + 'cardinality' => '0-d', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1400017', + 'subjectScheme' => 'NMRCV', + 'unitIri' => 'http://purl.allotrope.org/ontologies/result#AFR_0000186', + 'unitDisplay' => 'data points', + 'extractor' => 'numberOfPointsFromNmriumInfo', + 'kind' => 'numeric', + ], + [ + 'id' => 'nfdi.nmr.acquisition.temperature', + 'label' => 'Sample Temperature Information', + 'level' => 2, + 'cardinality' => '0-1', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1400262', + 'subjectScheme' => 'NMRCV', + 'unitIri' => 'http://purl.obolibrary.org/obo/UO_0000012', + 'unitDisplay' => 'K', + 'extractor' => 'temperatureFromNmriumInfo', + 'kind' => 'numeric', + ], + [ + 'id' => 'nfdi.nmr.acquisition.number_of_scans', + 'label' => 'Number of Scans', + 'level' => 2, + 'cardinality' => '0-1', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1400087', + 'subjectScheme' => 'NMRCV', + 'unitIri' => null, + 'unitDisplay' => 'scans', + 'extractor' => 'numberOfScansFromNmriumInfo', + 'kind' => 'numeric', + ], + [ + 'id' => 'nfdi.nmr.acquisition.spectral_width', + 'label' => 'Spectral Width', + 'level' => 2, + 'cardinality' => '0-d', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1000175', + 'subjectScheme' => 'NMRCV', + 'unitIri' => 'http://purl.obolibrary.org/obo/UO_0000106', + 'unitDisplay' => 'Hz', + 'extractor' => 'spectralWidthFromNmriumInfo', + 'kind' => 'numeric', + ], + + // 1.3 NMR Instrument + [ + 'id' => 'nfdi.nmr.instrument.probe', + 'label' => 'NMR Probe', + 'level' => 2, + 'cardinality' => '0-1', + 'ontologyIri' => 'http://nmrML.org/nmrCV#NMR_1400014', + 'subjectScheme' => 'NMRCV', + 'unitIri' => null, + 'unitDisplay' => null, + 'extractor' => 'probeFromNmriumInfo', + 'kind' => 'subject', + ], + ]; + } +} diff --git a/app/Support/Nmr/SolventChebiMap.php b/app/Support/Nmr/SolventChebiMap.php new file mode 100644 index 000000000..425b0117c --- /dev/null +++ b/app/Support/Nmr/SolventChebiMap.php @@ -0,0 +1,82 @@ + + */ + private const ENTRIES = [ + 'cdcl3' => 'http://purl.obolibrary.org/obo/CHEBI_85365', // chloroform-d + 'dmsod6' => 'http://purl.obolibrary.org/obo/CHEBI_193041', // DMSO-d6 + 'd2o' => 'http://purl.obolibrary.org/obo/CHEBI_41981', // deuterium oxide + 'meod' => 'http://purl.obolibrary.org/obo/CHEBI_85369', // methanol-d4 + 'cd3od' => 'http://purl.obolibrary.org/obo/CHEBI_85369', // methanol-d4 + 'methanold4' => 'http://purl.obolibrary.org/obo/CHEBI_85369', + 'c6d6' => 'http://purl.obolibrary.org/obo/CHEBI_85364', // benzene-d6 + 'benzened6' => 'http://purl.obolibrary.org/obo/CHEBI_85364', + 'acetoned6' => 'http://purl.obolibrary.org/obo/CHEBI_193039', // acetone-d6 + 'cd3cocd3' => 'http://purl.obolibrary.org/obo/CHEBI_193039', + 'thfd8' => 'http://purl.obolibrary.org/obo/CHEBI_193044', // THF-d8 + 'tolueneD8' => 'http://purl.obolibrary.org/obo/CHEBI_193040', // toluene-d8 + 'toluened8' => 'http://purl.obolibrary.org/obo/CHEBI_193040', + 'cd3cn' => 'http://purl.obolibrary.org/obo/CHEBI_177275', // acetonitrile-d3 + 'acetonitriled3' => 'http://purl.obolibrary.org/obo/CHEBI_177275', + 'pyridined5' => 'http://purl.obolibrary.org/obo/CHEBI_193043', // pyridine-d5 + ]; + + /** + * Resolve a solvent display name to its ChEBI IRI, or null when unknown. + */ + public static function lookup(?string $solventName): ?string + { + if ($solventName === null) { + return null; + } + + $key = self::normalize($solventName); + if ($key === '') { + return null; + } + + return self::ENTRIES[$key] ?? null; + } + + /** + * Normalize a free-text solvent name for lookup. Strips whitespace, + * underscores, hyphens, dots, and the common subscript characters + * (₀-₉) so "DMSO-d6", "DMSO d6", "DMSO_d6", and "dmsod6" all collapse + * to the same key. + */ + private static function normalize(string $name): string + { + $subscripts = ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉']; + $digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + $name = str_replace($subscripts, $digits, $name); + + $name = strtolower($name); + $name = preg_replace('/[\s\-_\.]+/', '', $name) ?? $name; + + return $name; + } +} diff --git a/app/Support/ProjectWorkspace.php b/app/Support/ProjectWorkspace.php new file mode 100644 index 000000000..b29b8b3ae --- /dev/null +++ b/app/Support/ProjectWorkspace.php @@ -0,0 +1,84 @@ +|null + */ + public static function memberWorkspaceContext(Request $request, Project $project, GetLicense $getLicense): ?array + { + $user = $request->user(); + if ($user === null || ! Gate::forUser($user)->check('viewProject', $project)) { + return null; + } + + $team = $project->nonPersonalTeam; + $license = null; + if ($project->license_id) { + $license = $getLicense->getLicensebyId($project->license_id); + } + + return [ + 'team' => $team ? $team->load(['users', 'owner']) : null, + 'members' => $project->allUsers(), + 'availableRoles' => array_values(Jetstream::$roles), + 'role' => $project->userProjectRole($user->email), + 'teamRole' => $user->belongsToTeam($team) ? $user->teamRole($team) : null, + 'license' => $license, + 'projectPermissions' => [ + 'canDeleteProject' => Gate::forUser($user)->check('deleteProject', $project), + 'canUpdateProject' => Gate::forUser($user)->check('updateProject', $project), + 'canManageSettings' => Gate::forUser($user)->check('manageSettings', $project), + ], + ]; + } + + /** + * Flat props merged into {@see ProjectController::show} (dashboard project home without a public id). + * + * @return array + */ + public static function dashboardShowCompanionProps(Request $request, Project $project, GetLicense $getLicense): array + { + return self::memberWorkspaceContext($request, $project, $getLicense) ?? []; + } + + /** + * Inertia props for authenticated project members on the unified public project UI. + * Empty when the viewer is a guest or is not allowed to view the project. + * + * @return array + */ + public static function inertiaPropsForPublicProject(Request $request, Project $project, GetLicense $getLicense): array + { + $ctx = self::memberWorkspaceContext($request, $project, $getLicense); + if ($ctx === null) { + return []; + } + + return [ + 'workspace' => array_merge($ctx, [ + 'preview' => false, + 'dashboardProject' => $project->load([ + 'projectInvitations', + 'tags', + 'authors', + 'citations', + 'owner', + 'draft', + ]), + ]), + ]; + } +} diff --git a/app/Support/ProvisionalDoi.php b/app/Support/ProvisionalDoi.php new file mode 100644 index 000000000..a2c1ddb28 --- /dev/null +++ b/app/Support/ProvisionalDoi.php @@ -0,0 +1,34 @@ +getAttribute('key'); + $segment = explode('-', $key, 2)[0] ?? ''; + + if ($segment === '') { + throw new RuntimeException('Draft key is missing.'); + } + + return $prefix.'/nmrxiv.'.$segment; + } +} diff --git a/app/Support/Public/PublicMoleculeAggregates.php b/app/Support/Public/PublicMoleculeAggregates.php new file mode 100644 index 000000000..7bb8be467 --- /dev/null +++ b/app/Support/Public/PublicMoleculeAggregates.php @@ -0,0 +1,247 @@ + $query + */ + public static function scopePublicCatalog(Builder $query): Builder + { + return $query + ->whereNotNull('identifier') + ->whereRaw(self::hasPublicSpectraExistsSql($query->getModel()->getTable().'.id')); + } + + /** + * @param array $molecules + * @return array + */ + public static function enrich(array $molecules): array + { + if ($molecules === []) { + return $molecules; + } + + $ids = array_values(array_unique(array_map( + fn ($molecule) => $molecule instanceof Molecule ? (int) $molecule->id : (int) $molecule->id, + $molecules, + ))); + + $sampleCounts = self::sampleCountsByMoleculeId($ids); + $experimentCounts = self::experimentTypeCountsByMoleculeId($ids); + + foreach ($molecules as $molecule) { + $id = $molecule instanceof Molecule ? (int) $molecule->id : (int) $molecule->id; + + $samples = $sampleCounts[$id] ?? 0; + $experiments = $experimentCounts[$id] ?? []; + + if ($molecule instanceof Molecule) { + $molecule->setAttribute('workspace_samples_count', $samples); + $molecule->setAttribute('workspace_experiment_type_counts', $experiments); + } else { + $molecule->workspace_samples_count = $samples; + $molecule->workspace_experiment_type_counts = $experiments; + } + } + + return $molecules; + } + + /** + * @param array $moleculeIds + * @return array + */ + private static function sampleCountsByMoleculeId(array $moleculeIds): array + { + if ($moleculeIds === []) { + return []; + } + + $placeholders = implode(',', array_fill(0, count($moleculeIds), '?')); + $datasetNotDeleted = self::datasetNotDeletedSql('d'); + $studyNotDeleted = '(st.is_deleted IS NULL OR st.is_deleted = false)'; + $nmriumSpectra = self::nmriumHasSpectraSql('n'); + + $rows = DB::select( + <<molecule_id] = (int) $row->sample_count; + } + + return $counts; + } + + /** + * @param array $moleculeIds + * @return array> + */ + private static function experimentTypeCountsByMoleculeId(array $moleculeIds): array + { + if ($moleculeIds === []) { + return []; + } + + $placeholders = implode(',', array_fill(0, count($moleculeIds), '?')); + $datasetNotDeleted = self::datasetNotDeletedSql('d'); + $studyNotDeleted = '(st.is_deleted IS NULL OR st.is_deleted = false)'; + $datasetSpectra = self::datasetHasSpectraSql('d', 'n'); + + $rows = DB::select( + << '' + AND {$datasetNotDeleted} + AND {$datasetSpectra} +GROUP BY ms.molecule_id, d.type +SQL, + array_merge([Dataset::class], $moleculeIds), + ); + + /** @var array> $grouped */ + $grouped = []; + + foreach ($rows as $row) { + $moleculeId = (int) $row->molecule_id; + $type = (string) $row->experiment_type; + $grouped[$moleculeId][$type] = (int) $row->dataset_count; + } + + return $grouped; + } + + private static function datasetHasSpectraSql(string $datasetAlias, string $nmriumAlias): string + { + $hasNmrium = "(COALESCE({$datasetAlias}.has_nmrium, false) = true)"; + + return match (DB::connection()->getDriverName()) { + 'pgsql' => "({$hasNmrium} OR ({$nmriumAlias}.id IS NOT NULL AND jsonb_array_length(COALESCE({$nmriumAlias}.nmrium_info->'data'->'spectra', '[]'::jsonb)) > 0))", + default => $hasNmrium, + }; + } + + private static function nmriumHasSpectraSql(string $nmriumAlias): string + { + return match (DB::connection()->getDriverName()) { + 'pgsql' => "jsonb_array_length(COALESCE({$nmriumAlias}.nmrium_info->'data'->'spectra', '[]'::jsonb)) > 0", + 'sqlite' => "json_array_length(COALESCE(json_extract({$nmriumAlias}.nmrium_info, '$.data.spectra'), '[]')) > 0", + default => '1=1', + }; + } + + private static function datasetNotDeletedSql(string $datasetAlias): string + { + return match (DB::connection()->getDriverName()) { + 'pgsql' => "(NOT COALESCE({$datasetAlias}.is_deleted, false))", + default => "({$datasetAlias}.is_deleted IS NULL OR {$datasetAlias}.is_deleted = 0 OR {$datasetAlias}.is_deleted = false)", + }; + } + + private static function sqlStringLiteral(string $value): string + { + return "'".str_replace("'", "''", $value)."'"; + } +} diff --git a/app/Support/Studies/ResetStudyCachedState.php b/app/Support/Studies/ResetStudyCachedState.php new file mode 100644 index 000000000..6927f7e57 --- /dev/null +++ b/app/Support/Studies/ResetStudyCachedState.php @@ -0,0 +1,132 @@ +download_url; + + if (! empty($previousUrl)) { + $this->deleteArchiveFromStorage($study, $previousUrl); + } + + $needsStudySave = $previousUrl !== null + || (bool) $study->has_nmrium + || $study->internal_status !== null + || (bool) $study->is_archived; + + if ($needsStudySave) { + $study->download_url = null; + $study->has_nmrium = false; + $study->internal_status = null; + $study->is_archived = false; + $study->saveQuietly(); + } + + $datasetIds = $study->datasets()->pluck('id')->all(); + + if (! empty($datasetIds)) { + Dataset::whereIn('id', $datasetIds) + ->where('has_nmrium', true) + ->update(['has_nmrium' => false]); + + NMRium::where('nmriumable_type', Dataset::class) + ->whereIn('nmriumable_id', $datasetIds) + ->delete(); + } + + NMRium::where('nmriumable_type', Study::class) + ->where('nmriumable_id', $study->id) + ->delete(); + } + + /** + * Best-effort deletion of the cached zip from object storage. Failures + * are logged but never raised — the in-DB reset has already succeeded + * and the zip will be overwritten on the next ArchiveStudy run anyway. + */ + private function deleteArchiveFromStorage(Study $study, string $url): void + { + $key = $this->extractStorageKeyFromUrl($url); + + if ($key === null) { + return; + } + + try { + $disk = config('filesystems.default'); + if (Storage::disk($disk)->exists($key)) { + Storage::disk($disk)->delete($key); + } + } catch (\Throwable $e) { + Log::warning('ResetStudyCachedState: failed to delete archive', [ + 'study_id' => $study->id, + 'key' => $key, + 'error' => $e->getMessage(), + ]); + } + } + + /** + * Strip the "{endpoint}/{bucket}/" prefix from a download URL to recover + * the storage key used by the configured disk. + */ + private function extractStorageKeyFromUrl(string $url): ?string + { + $disk = config('filesystems.default'); + $bucket = config("filesystems.disks.{$disk}.bucket"); + + if (! $bucket) { + return null; + } + + $marker = '/'.$bucket.'/'; + $position = strpos($url, $marker); + + if ($position === false) { + return null; + } + + $key = substr($url, $position + strlen($marker)); + + return $key !== '' ? $key : null; + } +} diff --git a/boost.json b/boost.json new file mode 100644 index 000000000..adddf2278 --- /dev/null +++ b/boost.json @@ -0,0 +1,11 @@ +{ + "agents": [ + "cursor" + ], + "editors": [ + "cursor", + "vscode" + ], + "guidelines": [], + "sail": true +} diff --git a/bootstrap/app.php b/bootstrap/app.php index a2f4fda87..33d43447e 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,16 +1,32 @@ withProviders([ - \SocialiteProviders\Manager\ServiceProvider::class, - \OwenIt\Auditing\AuditingServiceProvider::class, - \Lab404\Impersonate\ImpersonateServiceProvider::class, - \L5Swagger\L5SwaggerServiceProvider::class, + ServiceProvider::class, + AuditingServiceProvider::class, + ImpersonateServiceProvider::class, + L5SwaggerServiceProvider::class, ]) ->withRouting( web: __DIR__.'/../routes/web.php', @@ -28,24 +44,44 @@ 'support-bubble', ]); - $middleware->append(\Spatie\CookieConsent\CookieConsentMiddleware::class); + $middleware->append(CookieConsentMiddleware::class); $middleware->web([ - \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class, - \App\Http\Middleware\HandleInertiaRequests::class, - \App\Http\Middleware\XFrameOptions::class, + AuthenticateSession::class, + HandleInertiaRequests::class, + XFrameOptions::class, + AddCspHeaders::class, ]); - $middleware->throttleApi(); + // Disable API throttling in testing environment to allow test suite to run + if (env('APP_ENV') !== 'testing') { + $middleware->throttleApi(); + } - $middleware->replace(\Illuminate\Http\Middleware\TrustProxies::class, \App\Http\Middleware\TrustProxies::class); + $middleware->replace(TrustProxies::class, App\Http\Middleware\TrustProxies::class); $middleware->alias([ - 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, - 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, - 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class, + 'permission' => PermissionMiddleware::class, + 'role' => RoleMiddleware::class, + 'role_or_permission' => RoleOrPermissionMiddleware::class, ]); }) ->withExceptions(function (Exceptions $exceptions) { - // + $exceptions->render(function (ValidationException $e, Request $request) { + if (! $request->isMethod('POST') || ! $request->is('login')) { + return null; + } + + if ($request->expectsJson()) { + return null; + } + + return redirect()->route('login') + ->withInput(Arr::except($request->input(), [ + 'current_password', + 'password', + 'password_confirmation', + ])) + ->withErrors($e->errors(), $request->input('_error_bag', $e->errorBag)); + }); })->create(); diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 98112431b..d66cc53af 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -1,13 +1,25 @@ =8.1", - "psr/http-message": "^1.0 || ^2.0" + "psr/http-message": "^1.0 || ^2.0", + "symfony/filesystem": "^v5.4.45 || ^v6.4.3 || ^v7.1.0 || ^v8.0.0" }, "require-dev": { "andrewsville/php-token-reflection": "^1.4", "aws/aws-php-sns-message-validator": "~1.0", "behat/behat": "~3.0", "composer/composer": "^2.7.8", - "dms/phpunit-arraysubset-asserts": "^0.4.0", + "dms/phpunit-arraysubset-asserts": "^v0.5.0", "doctrine/cache": "~1.4", "ext-dom": "*", "ext-openssl": "*", - "ext-pcntl": "*", "ext-sockets": "*", - "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", + "phpunit/phpunit": "^10.0", "psr/cache": "^2.0 || ^3.0", "psr/simple-cache": "^2.0 || ^3.0", "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", - "symfony/filesystem": "^v6.4.0 || ^v7.1.0", "yoast/phpunit-polyfills": "^2.0" }, "suggest": { @@ -109,6 +108,7 @@ "doctrine/cache": "To use the DoctrineCacheAdapter", "ext-curl": "To send requests using cURL", "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-pcntl": "To use client-side monitoring", "ext-sockets": "To use client-side monitoring" }, "type": "library", @@ -135,11 +135,11 @@ "authors": [ { "name": "Amazon Web Services", - "homepage": "http://aws.amazon.com" + "homepage": "https://aws.amazon.com" } ], "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", - "homepage": "http://aws.amazon.com/sdkforphp", + "homepage": "https://aws.amazon.com/sdk-for-php", "keywords": [ "amazon", "aws", @@ -153,22 +153,22 @@ "support": { "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.365.0" + "source": "https://github.com/aws/aws-sdk-php/tree/3.382.2" }, - "time": "2025-12-02T16:06:36+00:00" + "time": "2026-05-27T18:11:41+00:00" }, { "name": "bacon/bacon-qr-code", - "version": "v3.0.3", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/Bacon/BaconQrCode.git", - "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563" + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/36a1cb2b81493fa5b82e50bf8068bf84d1542563", - "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", "shasum": "" }, "require": { @@ -208,22 +208,22 @@ "homepage": "https://github.com/Bacon/BaconQrCode", "support": { "issues": "https://github.com/Bacon/BaconQrCode/issues", - "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3" + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.1.1" }, - "time": "2025-11-19T17:15:36+00:00" + "time": "2026-04-05T21:06:35+00:00" }, { "name": "brick/math", - "version": "0.14.1", + "version": "0.14.8", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", + "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", "shasum": "" }, "require": { @@ -262,7 +262,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.1" + "source": "https://github.com/brick/math/tree/0.14.8" }, "funding": [ { @@ -270,7 +270,7 @@ "type": "github" } ], - "time": "2025-11-24T14:40:29+00:00" + "time": "2026-02-10T14:33:43+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -343,32 +343,33 @@ }, { "name": "darkaonline/l5-swagger", - "version": "8.6.5", + "version": "9.0.1", "source": { "type": "git", "url": "https://github.com/DarkaOnLine/L5-Swagger.git", - "reference": "4cf2b3faae9e9cffd05e4eb6e066741bf56f0a85" + "reference": "2c26427f8c41db8e72232415e7287313e6b6a2e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DarkaOnLine/L5-Swagger/zipball/4cf2b3faae9e9cffd05e4eb6e066741bf56f0a85", - "reference": "4cf2b3faae9e9cffd05e4eb6e066741bf56f0a85", + "url": "https://api.github.com/repos/DarkaOnLine/L5-Swagger/zipball/2c26427f8c41db8e72232415e7287313e6b6a2e2", + "reference": "2c26427f8c41db8e72232415e7287313e6b6a2e2", "shasum": "" }, "require": { "doctrine/annotations": "^1.0 || ^2.0", "ext-json": "*", - "laravel/framework": "^11.0 || ^10.0 || ^9.0 || >=8.40.0 || ^7.0", - "php": "^7.2 || ^8.0", - "swagger-api/swagger-ui": "^3.0 || >=4.1.3", + "laravel/framework": "^12.0 || ^11.0", + "php": "^8.2", + "swagger-api/swagger-ui": ">=5.18.3", "symfony/yaml": "^5.0 || ^6.0 || ^7.0", - "zircote/swagger-php": "^3.2.0 || ^4.0.0" + "zircote/swagger-php": "^5.0.0" }, "require-dev": { "mockery/mockery": "1.*", - "orchestra/testbench": "^9.0 || ^8.0 || 7.* || ^6.15 || 5.*", + "orchestra/testbench": "^10.0 || ^9.0 || ^8.0 || 7.* || ^6.15 || 5.*", "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^11.0 || ^10.0 || ^9.5" + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { @@ -411,7 +412,7 @@ ], "support": { "issues": "https://github.com/DarkaOnLine/L5-Swagger/issues", - "source": "https://github.com/DarkaOnLine/L5-Swagger/tree/8.6.5" + "source": "https://github.com/DarkaOnLine/L5-Swagger/tree/9.0.1" }, "funding": [ { @@ -419,7 +420,7 @@ "type": "github" } ], - "time": "2025-02-06T14:54:32+00:00" + "time": "2025-02-28T06:25:02+00:00" }, { "name": "dasprid/enum", @@ -625,16 +626,16 @@ }, { "name": "doctrine/dbal", - "version": "3.10.4", + "version": "3.10.5", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "63a46cb5aa6f60991186cc98c1d1b50c09311868" + "reference": "95d84866bf3c04b2ddca1df7c049714660959aef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/63a46cb5aa6f60991186cc98c1d1b50c09311868", - "reference": "63a46cb5aa6f60991186cc98c1d1b50c09311868", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/95d84866bf3c04b2ddca1df7c049714660959aef", + "reference": "95d84866bf3c04b2ddca1df7c049714660959aef", "shasum": "" }, "require": { @@ -655,9 +656,9 @@ "jetbrains/phpstorm-stubs": "2023.1", "phpstan/phpstan": "2.1.30", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "9.6.29", - "slevomat/coding-standard": "8.24.0", - "squizlabs/php_codesniffer": "4.0.0", + "phpunit/phpunit": "9.6.34", + "slevomat/coding-standard": "8.27.1", + "squizlabs/php_codesniffer": "4.0.1", "symfony/cache": "^5.4|^6.0|^7.0|^8.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0|^8.0" }, @@ -719,7 +720,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.10.4" + "source": "https://github.com/doctrine/dbal/tree/3.10.5" }, "funding": [ { @@ -735,33 +736,33 @@ "type": "tidelift" } ], - "time": "2025-11-29T10:46:08+00:00" + "time": "2026-02-24T08:03:57+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -781,22 +782,22 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "doctrine/event-manager", - "version": "2.0.1", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", - "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf", "shasum": "" }, "require": { @@ -806,10 +807,10 @@ "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" + "doctrine/coding-standard": "^14", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/phpstan": "^2.1.32", + "phpunit/phpunit": "^10.5.58" }, "type": "library", "autoload": { @@ -858,7 +859,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.1" + "source": "https://github.com/doctrine/event-manager/tree/2.1.1" }, "funding": [ { @@ -874,7 +875,7 @@ "type": "tidelift" } ], - "time": "2024-05-22T20:47:39+00:00" + "time": "2026-01-29T07:11:08+00:00" }, { "name": "doctrine/inflector", @@ -1176,16 +1177,16 @@ }, { "name": "firebase/php-jwt", - "version": "v6.11.1", + "version": "v7.0.5", "source": { "type": "git", - "url": "https://github.com/firebase/php-jwt.git", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + "url": "https://github.com/googleapis/php-jwt.git", + "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/47ad26bab5e7c70ae8a6f08ed25ff83631121380", + "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380", "shasum": "" }, "require": { @@ -1193,6 +1194,7 @@ }, "require-dev": { "guzzlehttp/guzzle": "^7.4", + "phpfastcache/phpfastcache": "^9.2", "phpspec/prophecy-phpunit": "^2.0", "phpunit/phpunit": "^9.5", "psr/cache": "^2.0||^3.0", @@ -1232,10 +1234,10 @@ "php" ], "support": { - "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + "issues": "https://github.com/googleapis/php-jwt/issues", + "source": "https://github.com/googleapis/php-jwt/tree/v7.0.5" }, - "time": "2025-04-09T20:32:01+00:00" + "time": "2026-04-01T20:38:03+00:00" }, { "name": "fruitcake/php-cors", @@ -1310,24 +1312,24 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.1.3", + "version": "v1.1.4", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3" + "phpoption/phpoption": "^1.9.5" }, "require-dev": { - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" }, "type": "library", "autoload": { @@ -1356,7 +1358,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" }, "funding": [ { @@ -1368,20 +1370,20 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:45:45+00:00" + "time": "2025-12-27T19:43:20+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.10.0", + "version": "7.10.5", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + "reference": "7c8d84b39e680315f687e8662a9d6fb0865c5148" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7c8d84b39e680315f687e8662a9d6fb0865c5148", + "reference": "7c8d84b39e680315f687e8662a9d6fb0865c5148", "shasum": "" }, "require": { @@ -1399,8 +1401,9 @@ "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.4", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1478,7 +1481,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + "source": "https://github.com/guzzle/guzzle/tree/7.10.5" }, "funding": [ { @@ -1494,20 +1497,20 @@ "type": "tidelift" } ], - "time": "2025-08-23T22:36:01+00:00" + "time": "2026-05-27T11:53:46+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.3.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" + "reference": "09e8a212562fb1fb6a512c4156ed71525969d6c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "url": "https://api.github.com/repos/guzzle/promises/zipball/09e8a212562fb1fb6a512c4156ed71525969d6c2", + "reference": "09e8a212562fb1fb6a512c4156ed71525969d6c2", "shasum": "" }, "require": { @@ -1515,7 +1518,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { @@ -1561,7 +1564,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" + "source": "https://github.com/guzzle/promises/tree/2.4.1" }, "funding": [ { @@ -1577,20 +1580,20 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:34:08+00:00" + "time": "2026-05-20T22:57:30+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.8.0", + "version": "2.10.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "21dc724a0583619cd1652f673303492272778051" + "reference": "7c1472269227dc6f18930bd903d7a88fe6c52130" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", - "reference": "21dc724a0583619cd1652f673303492272778051", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7c1472269227dc6f18930bd903d7a88fe6c52130", + "reference": "7c1472269227dc6f18930bd903d7a88fe6c52130", "shasum": "" }, "require": { @@ -1605,8 +1608,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1677,7 +1681,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.8.0" + "source": "https://github.com/guzzle/psr7/tree/2.10.3" }, "funding": [ { @@ -1693,20 +1697,20 @@ "type": "tidelift" } ], - "time": "2025-08-23T21:21:41+00:00" + "time": "2026-05-27T11:48:20+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.5", + "version": "v1.0.6", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + "reference": "eef7f87bab6f204eba3c39224d8075c70c637946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/eef7f87bab6f204eba3c39224d8075c70c637946", + "reference": "eef7f87bab6f204eba3c39224d8075c70c637946", "shasum": "" }, "require": { @@ -1715,7 +1719,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1763,7 +1767,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.6" }, "funding": [ { @@ -1779,20 +1783,20 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:27:06+00:00" + "time": "2026-05-23T22:00:21+00:00" }, { "name": "http-interop/http-factory-guzzle", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/http-interop/http-factory-guzzle.git", - "reference": "8f06e92b95405216b237521cc64c804dd44c4a81" + "reference": "c2c859ceb05c3f42e710b60555f4c35b6a4a3995" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/8f06e92b95405216b237521cc64c804dd44c4a81", - "reference": "8f06e92b95405216b237521cc64c804dd44c4a81", + "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/c2c859ceb05c3f42e710b60555f4c35b6a4a3995", + "reference": "c2c859ceb05c3f42e710b60555f4c35b6a4a3995", "shasum": "" }, "require": { @@ -1835,37 +1839,37 @@ ], "support": { "issues": "https://github.com/http-interop/http-factory-guzzle/issues", - "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.2.0" + "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.2.1" }, - "time": "2021-07-21T13:50:14+00:00" + "time": "2025-12-15T11:28:16+00:00" }, { "name": "inertiajs/inertia-laravel", - "version": "v2.0.11", + "version": "v2.0.21", "source": { "type": "git", "url": "https://github.com/inertiajs/inertia-laravel.git", - "reference": "041e148b3228407b5abe584a4f02df2651ab4d85" + "reference": "fabf202a29023de5f2d59f1474271fa35f84f1d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/041e148b3228407b5abe584a4f02df2651ab4d85", - "reference": "041e148b3228407b5abe584a4f02df2651ab4d85", + "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/fabf202a29023de5f2d59f1474271fa35f84f1d4", + "reference": "fabf202a29023de5f2d59f1474271fa35f84f1d4", "shasum": "" }, "require": { "ext-json": "*", - "laravel/framework": "^10.0|^11.0|^12.0", + "laravel/framework": "^10.0|^11.0|^12.0|^13.0", "php": "^8.1.0", - "symfony/console": "^6.2|^7.0" + "symfony/console": "^6.2|^7.0|^8.0" }, "require-dev": { "guzzlehttp/guzzle": "^7.2", "larastan/larastan": "^3.0", "laravel/pint": "^1.16", "mockery/mockery": "^1.3.3", - "orchestra/testbench": "^8.0|^9.2|^10.0", - "phpunit/phpunit": "^10.4|^11.5", + "orchestra/testbench": "^8.0|^9.2|^10.0|^11.0", + "phpunit/phpunit": "^10.4|^11.5|^12.0", "roave/security-advisories": "dev-master" }, "suggest": { @@ -1905,31 +1909,31 @@ ], "support": { "issues": "https://github.com/inertiajs/inertia-laravel/issues", - "source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.11" + "source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.21" }, - "time": "2025-11-26T23:07:12+00:00" + "time": "2026-02-24T20:21:28+00:00" }, { "name": "lab404/laravel-impersonate", - "version": "1.7.7", + "version": "1.7.8", "source": { "type": "git", "url": "https://github.com/404labfr/laravel-impersonate.git", - "reference": "5033f3433a55ca8bb2cc3e4a018a39dd8a327a9f" + "reference": "0008a39da8914cc946b6a5ed211230708ee736b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/404labfr/laravel-impersonate/zipball/5033f3433a55ca8bb2cc3e4a018a39dd8a327a9f", - "reference": "5033f3433a55ca8bb2cc3e4a018a39dd8a327a9f", + "url": "https://api.github.com/repos/404labfr/laravel-impersonate/zipball/0008a39da8914cc946b6a5ed211230708ee736b3", + "reference": "0008a39da8914cc946b6a5ed211230708ee736b3", "shasum": "" }, "require": { - "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0", + "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0 | ^13.0", "php": "^7.2 | ^8.0" }, "require-dev": { "mockery/mockery": "^1.3.3", - "orchestra/testbench": "^4.0 | ^5.0 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", + "orchestra/testbench": "^4.0 | ^5.0 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0", "phpunit/phpunit": "^7.5 | ^8.0 | ^9.0 | ^10.0 | ^11.0" }, "type": "library", @@ -1972,9 +1976,9 @@ ], "support": { "issues": "https://github.com/404labfr/laravel-impersonate/issues", - "source": "https://github.com/404labfr/laravel-impersonate/tree/1.7.7" + "source": "https://github.com/404labfr/laravel-impersonate/tree/1.7.8" }, - "time": "2025-02-24T16:18:38+00:00" + "time": "2026-03-17T15:24:14+00:00" }, { "name": "laminas/laminas-diactoros", @@ -2064,92 +2068,31 @@ ], "time": "2025-10-12T15:31:36+00:00" }, - { - "name": "larabug/larabug", - "version": "3.3", - "source": { - "type": "git", - "url": "https://github.com/LaraBug/LaraBug.git", - "reference": "e9dba4d38166372f3772b57f39296eb502c598d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/LaraBug/LaraBug/zipball/e9dba4d38166372f3772b57f39296eb502c598d1", - "reference": "e9dba4d38166372f3772b57f39296eb502c598d1", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "^6.0.2 || ^7.0", - "illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", - "nesbot/carbon": "^2.62.1 || ^3.0", - "php": "^7.4 || ^8.0 || ^8.2 || ^8.3 || ^8.4" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.4", - "mockery/mockery": "^1.3.3 || ^1.4.2", - "orchestra/testbench": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0", - "phpunit/phpunit": "^8.5.23 || ^9.5.12 || ^10.0.9 || ^11.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "LaraBug\\ServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "LaraBug\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nathan Geerinck", - "email": "nathan@intilli.be", - "role": "Owner" - } - ], - "description": "Laravel 6.x/7.x/8.x/9.x/10.x/11.x bug notifier", - "keywords": [ - "error", - "laravel", - "log" - ], - "support": { - "issues": "https://github.com/LaraBug/LaraBug/issues", - "source": "https://github.com/LaraBug/LaraBug/tree/3.3" - }, - "time": "2025-03-01T16:12:03+00:00" - }, { "name": "laravel/fortify", - "version": "v1.32.1", + "version": "v1.37.2", "source": { "type": "git", "url": "https://github.com/laravel/fortify.git", - "reference": "26db37ed915770e5f0f91f6b7e153d9b3e6c09e8" + "reference": "5d4b6a53527edd19ecc4f13e8e74ec91bdefab0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/26db37ed915770e5f0f91f6b7e153d9b3e6c09e8", - "reference": "26db37ed915770e5f0f91f6b7e153d9b3e6c09e8", + "url": "https://api.github.com/repos/laravel/fortify/zipball/5d4b6a53527edd19ecc4f13e8e74ec91bdefab0c", + "reference": "5d4b6a53527edd19ecc4f13e8e74ec91bdefab0c", "shasum": "" }, "require": { "bacon/bacon-qr-code": "^3.0", "ext-json": "*", - "illuminate/support": "^10.0|^11.0|^12.0", - "php": "^8.1", - "pragmarx/google2fa": "^9.0", - "symfony/console": "^6.0|^7.0" + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "laravel/passkeys": "^0.2.0", + "php": "^8.2", + "pragmarx/google2fa": "^9.0" }, "require-dev": { - "orchestra/testbench": "^8.36|^9.15|^10.8", + "orchestra/testbench": "^9.15|^10.8|^11.0", "phpstan/phpstan": "^1.10" }, "type": "library", @@ -2187,20 +2130,20 @@ "issues": "https://github.com/laravel/fortify/issues", "source": "https://github.com/laravel/fortify" }, - "time": "2025-11-21T01:53:28+00:00" + "time": "2026-05-15T22:59:10+00:00" }, { "name": "laravel/framework", - "version": "v12.41.1", + "version": "v12.61.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "3e229b05935fd0300c632fb1f718c73046d664fc" + "reference": "1124062a1ca92d290c8bcb9b7f649920fa6816bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/3e229b05935fd0300c632fb1f718c73046d664fc", - "reference": "3e229b05935fd0300c632fb1f718c73046d664fc", + "url": "https://api.github.com/repos/laravel/framework/zipball/1124062a1ca92d290c8bcb9b7f649920fa6816bf", + "reference": "1124062a1ca92d290c8bcb9b7f649920fa6816bf", "shasum": "" }, "require": { @@ -2221,7 +2164,7 @@ "guzzlehttp/uri-template": "^1.0", "laravel/prompts": "^0.3.0", "laravel/serializable-closure": "^1.3|^2.0", - "league/commonmark": "^2.7", + "league/commonmark": "^2.8.1", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", @@ -2241,8 +2184,8 @@ "symfony/mailer": "^7.2.0", "symfony/mime": "^7.2.0", "symfony/polyfill-php83": "^1.33", - "symfony/polyfill-php84": "^1.33", - "symfony/polyfill-php85": "^1.33", + "symfony/polyfill-php84": "^1.34", + "symfony/polyfill-php85": "^1.34", "symfony/process": "^7.2.0", "symfony/routing": "^7.2.0", "symfony/uid": "^7.2.0", @@ -2288,6 +2231,7 @@ "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", "illuminate/routing": "self.version", "illuminate/session": "self.version", "illuminate/support": "self.version", @@ -2312,10 +2256,10 @@ "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", "opis/json-schema": "^2.4.1", - "orchestra/testbench-core": "^10.8.0", + "orchestra/testbench-core": "^10.9.0", "pda/pheanstalk": "^5.0.6|^7.0.0", "php-http/discovery": "^1.15", - "phpstan/phpstan": "^2.0", + "phpstan/phpstan": "^2.1.41", "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", "predis/predis": "^2.3|^3.0", "resend/resend-php": "^0.10.0|^1.0", @@ -2374,6 +2318,7 @@ "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", "src/Illuminate/Support/functions.php", "src/Illuminate/Support/helpers.php" ], @@ -2382,7 +2327,8 @@ "Illuminate\\Support\\": [ "src/Illuminate/Macroable/", "src/Illuminate/Collections/", - "src/Illuminate/Conditionable/" + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" ] } }, @@ -2406,40 +2352,41 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-12-03T01:02:13+00:00" + "time": "2026-05-26T23:41:33+00:00" }, { "name": "laravel/horizon", - "version": "v5.40.2", + "version": "v5.47.1", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "005e5638478db9e25f7ae5cfb30c56846fbad793" + "reference": "356e3ec86321361a4cb8259cb73fdb3406fa3b30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/005e5638478db9e25f7ae5cfb30c56846fbad793", - "reference": "005e5638478db9e25f7ae5cfb30c56846fbad793", + "url": "https://api.github.com/repos/laravel/horizon/zipball/356e3ec86321361a4cb8259cb73fdb3406fa3b30", + "reference": "356e3ec86321361a4cb8259cb73fdb3406fa3b30", "shasum": "" }, "require": { "ext-json": "*", "ext-pcntl": "*", "ext-posix": "*", - "illuminate/contracts": "^9.21|^10.0|^11.0|^12.0", - "illuminate/queue": "^9.21|^10.0|^11.0|^12.0", - "illuminate/support": "^9.21|^10.0|^11.0|^12.0", + "illuminate/contracts": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/queue": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0", + "laravel/sentinel": "^1.0", "nesbot/carbon": "^2.17|^3.0", "php": "^8.0", "ramsey/uuid": "^4.0", - "symfony/console": "^6.0|^7.0", - "symfony/error-handler": "^6.0|^7.0", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/error-handler": "^6.0|^7.0|^8.0", "symfony/polyfill-php83": "^1.28", - "symfony/process": "^6.0|^7.0" + "symfony/process": "^6.0|^7.0|^8.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.55|^8.36|^9.15|^10.8", + "orchestra/testbench": "^7.56|^8.37|^9.16|^10.9|^11.0", "phpstan/phpstan": "^1.10|^2.0", "predis/predis": "^1.1|^2.0|^3.0" }, @@ -2483,39 +2430,39 @@ ], "support": { "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.40.2" + "source": "https://github.com/laravel/horizon/tree/v5.47.1" }, - "time": "2025-11-28T20:12:12+00:00" + "time": "2026-05-20T12:12:55+00:00" }, { "name": "laravel/jetstream", - "version": "v5.4.0", + "version": "v5.5.3", "source": { "type": "git", "url": "https://github.com/laravel/jetstream.git", - "reference": "5a83a20b419c2b2c42a8c2407ca4647d8734456b" + "reference": "61cac5cde455311890f6981fb2da47acd298e4e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/jetstream/zipball/5a83a20b419c2b2c42a8c2407ca4647d8734456b", - "reference": "5a83a20b419c2b2c42a8c2407ca4647d8734456b", + "url": "https://api.github.com/repos/laravel/jetstream/zipball/61cac5cde455311890f6981fb2da47acd298e4e2", + "reference": "61cac5cde455311890f6981fb2da47acd298e4e2", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/console": "^11.0|^12.0", - "illuminate/support": "^11.0|^12.0", + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", "laravel/fortify": "^1.20", "mobiledetect/mobiledetectlib": "^4.8.08", "php": "^8.2.0", - "symfony/console": "^7.0" + "symfony/console": "^7.0|^8.0" }, "require-dev": { "inertiajs/inertia-laravel": "^2.0", "laravel/sanctum": "^4.0", "livewire/livewire": "^3.3", "mockery/mockery": "^1.0", - "orchestra/testbench": "^9.15|^10.8", + "orchestra/testbench": "^9.15|^10.8|^11.0", "phpstan/phpstan": "^1.10" }, "type": "library", @@ -2551,31 +2498,31 @@ "issues": "https://github.com/laravel/jetstream/issues", "source": "https://github.com/laravel/jetstream" }, - "time": "2025-11-25T14:46:14+00:00" + "time": "2026-05-19T01:30:03+00:00" }, { "name": "laravel/octane", - "version": "v2.13.2", + "version": "v2.17.4", "source": { "type": "git", "url": "https://github.com/laravel/octane.git", - "reference": "5b963d2da879f2cad3a84f22bafd3d8be7170988" + "reference": "ffeac11324accc6edf8df426ba4541f3171d494e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/octane/zipball/5b963d2da879f2cad3a84f22bafd3d8be7170988", - "reference": "5b963d2da879f2cad3a84f22bafd3d8be7170988", + "url": "https://api.github.com/repos/laravel/octane/zipball/ffeac11324accc6edf8df426ba4541f3171d494e", + "reference": "ffeac11324accc6edf8df426ba4541f3171d494e", "shasum": "" }, "require": { "laminas/laminas-diactoros": "^3.0", - "laravel/framework": "^10.10.1|^11.0|^12.0", + "laravel/framework": "^10.10.1|^11.0|^12.0|^13.0", "laravel/prompts": "^0.1.24|^0.2.0|^0.3.0", "laravel/serializable-closure": "^1.3|^2.0", "nesbot/carbon": "^2.66.0|^3.0", "php": "^8.1.0", - "symfony/console": "^6.0|^7.0", - "symfony/psr-http-message-bridge": "^2.2.0|^6.4|^7.0" + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/psr-http-message-bridge": "^2.2.0|^6.4|^7.0|^8.0" }, "conflict": { "spiral/roadrunner": "<2023.1.0", @@ -2588,11 +2535,10 @@ "laravel/scout": "^10.2.1", "laravel/socialite": "^5.6.1", "livewire/livewire": "^2.12.3|^3.0", - "mockery/mockery": "^1.5.1", "nunomaduro/collision": "^6.4.0|^7.5.2|^8.0", - "orchestra/testbench": "^8.21|^9.0|^10.0", + "orchestra/testbench": "^8.21|^9.0|^10.0|^11.0", "phpstan/phpstan": "^2.1.7", - "phpunit/phpunit": "^10.4|^11.5", + "phpunit/phpunit": "^10.4|^11.5|^12.0|^13.0", "spiral/roadrunner-cli": "^2.6.0", "spiral/roadrunner-http": "^3.3.0" }, @@ -2641,34 +2587,102 @@ "issues": "https://github.com/laravel/octane/issues", "source": "https://github.com/laravel/octane" }, - "time": "2025-11-28T20:13:00+00:00" + "time": "2026-05-22T11:30:06+00:00" + }, + { + "name": "laravel/passkeys", + "version": "v0.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/passkeys-server.git", + "reference": "a76656ada41b2b4a591f075eddae5ddc67e8ab9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/passkeys-server/zipball/a76656ada41b2b4a591f075eddae5ddc67e8ab9c", + "reference": "a76656ada41b2b4a591f075eddae5ddc67e8ab9c", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^11.0|^12.0|^13.0", + "illuminate/database": "^11.0|^12.0|^13.0", + "illuminate/http": "^11.0|^12.0|^13.0", + "illuminate/routing": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "php": "^8.2", + "web-auth/webauthn-lib": "5.3.x" + }, + "require-dev": { + "laravel/pint": "^1.28.0", + "orchestra/testbench": "^9.0|^10.0|^11.0", + "pestphp/pest": "^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "rector/rector": "^2.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Passkeys\\PasskeysServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Passkeys\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Passwordless authentication using WebAuthn/passkeys for Laravel", + "homepage": "https://github.com/laravel/passkeys-server", + "keywords": [ + "Authentication", + "Passwordless", + "laravel", + "passkeys", + "webauthn" + ], + "support": { + "issues": "https://github.com/laravel/passkeys-server/issues", + "source": "https://github.com/laravel/passkeys-server" + }, + "time": "2026-05-18T16:26:00+00:00" }, { "name": "laravel/prompts", - "version": "v0.3.8", + "version": "v0.3.18", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "096748cdfb81988f60090bbb839ce3205ace0d35" + "reference": "a19af51bb144bf87f08397921fa619f85c7d4e72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/096748cdfb81988f60090bbb839ce3205ace0d35", - "reference": "096748cdfb81988f60090bbb839ce3205ace0d35", + "url": "https://api.github.com/repos/laravel/prompts/zipball/a19af51bb144bf87f08397921fa619f85c7d4e72", + "reference": "a19af51bb144bf87f08397921fa619f85c7d4e72", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", "ext-mbstring": "*", "php": "^8.1", - "symfony/console": "^6.2|^7.0" + "symfony/console": "^6.2|^7.0|^8.0" }, "conflict": { "illuminate/console": ">=10.17.0 <10.25.0", "laravel/framework": ">=10.17.0 <10.25.0" }, "require-dev": { - "illuminate/collections": "^10.0|^11.0|^12.0", + "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", "mockery/mockery": "^1.5", "pestphp/pest": "^2.3|^3.4|^4.0", "phpstan/phpstan": "^1.12.28", @@ -2698,36 +2712,36 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.8" + "source": "https://github.com/laravel/prompts/tree/v0.3.18" }, - "time": "2025-11-21T20:52:52+00:00" + "time": "2026-05-19T00:47:18+00:00" }, { "name": "laravel/sanctum", - "version": "v4.2.1", + "version": "v4.3.2", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "f5fb373be39a246c74a060f2cf2ae2c2145b3664" + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/f5fb373be39a246c74a060f2cf2ae2c2145b3664", - "reference": "f5fb373be39a246c74a060f2cf2ae2c2145b3664", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/2a9bccc18e9907808e0018dd15fa643937886b1e", + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/console": "^11.0|^12.0", - "illuminate/contracts": "^11.0|^12.0", - "illuminate/database": "^11.0|^12.0", - "illuminate/support": "^11.0|^12.0", + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/contracts": "^11.0|^12.0|^13.0", + "illuminate/database": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", "php": "^8.2", - "symfony/console": "^7.0" + "symfony/console": "^7.0|^8.0" }, "require-dev": { "mockery/mockery": "^1.6", - "orchestra/testbench": "^9.15|^10.8", + "orchestra/testbench": "^9.15|^10.8|^11.0", "phpstan/phpstan": "^1.10" }, "type": "library", @@ -2763,32 +2777,32 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2025-11-21T13:59:03+00:00" + "time": "2026-04-30T11:46:25+00:00" }, { "name": "laravel/scout", - "version": "v10.22.1", + "version": "v10.25.0", "source": { "type": "git", "url": "https://github.com/laravel/scout.git", - "reference": "13ed8e0eeaddd894bf360b85cb873980de19dbaf" + "reference": "f28630dca44a63d80c15e596bedc5af42c8985d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/scout/zipball/13ed8e0eeaddd894bf360b85cb873980de19dbaf", - "reference": "13ed8e0eeaddd894bf360b85cb873980de19dbaf", + "url": "https://api.github.com/repos/laravel/scout/zipball/f28630dca44a63d80c15e596bedc5af42c8985d5", + "reference": "f28630dca44a63d80c15e596bedc5af42c8985d5", "shasum": "" }, "require": { - "illuminate/bus": "^9.0|^10.0|^11.0|^12.0", - "illuminate/contracts": "^9.0|^10.0|^11.0|^12.0", - "illuminate/database": "^9.0|^10.0|^11.0|^12.0", - "illuminate/http": "^9.0|^10.0|^11.0|^12.0", - "illuminate/pagination": "^9.0|^10.0|^11.0|^12.0", - "illuminate/queue": "^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^9.0|^10.0|^11.0|^12.0", + "illuminate/bus": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/database": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/http": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/pagination": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/queue": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", - "symfony/console": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0" }, "conflict": { "algolia/algoliasearch-client-php": "<3.2.0|>=5.0.0" @@ -2797,7 +2811,7 @@ "algolia/algoliasearch-client-php": "^3.2|^4.0", "meilisearch/meilisearch-php": "^1.0", "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.31|^8.36|^9.15|^10.8", + "orchestra/testbench": "^7.31|^8.36|^9.15|^10.8|^11.0", "php-http/guzzle7-adapter": "^1.0", "phpstan/phpstan": "^1.10", "typesense/typesense-php": "^4.9.3" @@ -2843,31 +2857,87 @@ "issues": "https://github.com/laravel/scout/issues", "source": "https://github.com/laravel/scout" }, - "time": "2025-11-25T15:19:35+00:00" + "time": "2026-03-10T16:16:46+00:00" + }, + { + "name": "laravel/sentinel", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sentinel.git", + "reference": "972d9885d9d14312a118e9565c4e6ecc5e751ea1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sentinel/zipball/972d9885d9d14312a118e9565c4e6ecc5e751ea1", + "reference": "972d9885d9d14312a118e9565c4e6ecc5e751ea1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/container": "^8.37|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0" + }, + "require-dev": { + "laravel/pint": "^1.27", + "orchestra/testbench": "^6.47.1|^7.56|^8.37|^9.16|^10.9|^11.0", + "phpstan/phpstan": "^2.1.33" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sentinel\\SentinelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sentinel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Mior Muhammad Zaki", + "email": "mior@laravel.com" + } + ], + "support": { + "source": "https://github.com/laravel/sentinel/tree/v1.1.0" + }, + "time": "2026-03-24T14:03:38+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.7", + "version": "v2.0.13", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", - "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { - "illuminate/support": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "nesbot/carbon": "^2.67|^3.0", "pestphp/pest": "^2.36|^3.0|^4.0", "phpstan/phpstan": "^2.0", - "symfony/var-dumper": "^6.2.0|^7.0.0" + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" }, "type": "library", "extra": { @@ -2904,36 +2974,36 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-11-21T20:52:36+00:00" + "time": "2026-04-16T14:03:50+00:00" }, { "name": "laravel/socialite", - "version": "v5.23.2", + "version": "v5.27.0", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "41e65d53762d33d617bf0253330d672cb95e624b" + "reference": "40e0757a75637c7b2dff05d3286b0d8fc25e5c0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/41e65d53762d33d617bf0253330d672cb95e624b", - "reference": "41e65d53762d33d617bf0253330d672cb95e624b", + "url": "https://api.github.com/repos/laravel/socialite/zipball/40e0757a75637c7b2dff05d3286b0d8fc25e5c0e", + "reference": "40e0757a75637c7b2dff05d3286b0d8fc25e5c0e", "shasum": "" }, "require": { "ext-json": "*", - "firebase/php-jwt": "^6.4", + "firebase/php-jwt": "^6.4|^7.0", "guzzlehttp/guzzle": "^6.0|^7.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "league/oauth1-client": "^1.11", "php": "^7.2|^8.0", "phpseclib/phpseclib": "^3.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^4.18|^5.20|^6.47|^7.55|^8.36|^9.15|^10.8", + "orchestra/testbench": "^4.18|^5.20|^6.47|^7.55|^8.36|^9.15|^10.8|^11.0", "phpstan/phpstan": "^1.12.23", "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5|^12.0" }, @@ -2976,20 +3046,20 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2025-11-21T14:00:38+00:00" + "time": "2026-04-24T14:05:47+00:00" }, { "name": "laravel/tinker", - "version": "v2.10.2", + "version": "v2.11.1", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c" + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/3bcb5f62d6f837e0f093a601e26badafb127bd4c", - "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c", + "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741", + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741", "shasum": "" }, "require": { @@ -2998,7 +3068,7 @@ "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "php": "^7.2.5|^8.0", "psy/psysh": "^0.11.1|^0.12.0", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", @@ -3040,22 +3110,22 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.2" + "source": "https://github.com/laravel/tinker/tree/v2.11.1" }, - "time": "2025-11-20T16:29:12+00:00" + "time": "2026-02-06T14:12:35+00:00" }, { "name": "league/commonmark", - "version": "2.8.0", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb" + "reference": "59fb075d2101740c337c7216e3f32b36c204218b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/4efa10c1e56488e658d10adf7b7b7dcd19940bfb", - "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b", "shasum": "" }, "require": { @@ -3080,9 +3150,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 | ^7.0", - "symfony/process": "^5.4 | ^6.0 | ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, @@ -3149,7 +3219,7 @@ "type": "tidelift" } ], - "time": "2025-11-26T21:48:24+00:00" + "time": "2026-03-19T13:16:38+00:00" }, { "name": "league/config", @@ -3235,16 +3305,16 @@ }, { "name": "league/flysystem", - "version": "3.30.2", + "version": "3.34.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277" + "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", - "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", + "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", "shasum": "" }, "require": { @@ -3312,26 +3382,26 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.30.2" + "source": "https://github.com/thephpleague/flysystem/tree/3.34.0" }, - "time": "2025-11-10T17:13:11+00:00" + "time": "2026-05-14T10:28:08+00:00" }, { "name": "league/flysystem-aws-s3-v3", - "version": "3.30.1", + "version": "3.34.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "d286e896083bed3190574b8b088b557b59eb66f5" + "reference": "0c62fdac907791d8649ad3c61cb7a77628344fb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/d286e896083bed3190574b8b088b557b59eb66f5", - "reference": "d286e896083bed3190574b8b088b557b59eb66f5", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/0c62fdac907791d8649ad3c61cb7a77628344fb8", + "reference": "0c62fdac907791d8649ad3c61cb7a77628344fb8", "shasum": "" }, "require": { - "aws/aws-sdk-php": "^3.295.10", + "aws/aws-sdk-php": "^3.371.5", "league/flysystem": "^3.10.0", "league/mime-type-detection": "^1.0.0", "php": "^8.0.2" @@ -3367,22 +3437,22 @@ "storage" ], "support": { - "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.30.1" + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.34.0" }, - "time": "2025-10-20T15:27:33+00:00" + "time": "2026-05-04T08:24:00+00:00" }, { "name": "league/flysystem-local", - "version": "3.30.2", + "version": "3.31.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d" + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ab4f9d0d672f601b102936aa728801dd1a11968d", - "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", "shasum": "" }, "require": { @@ -3416,22 +3486,22 @@ "local" ], "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.2" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" }, - "time": "2025-11-10T11:23:37+00:00" + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/flysystem-ziparchive", - "version": "3.29.0", + "version": "3.31.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-ziparchive.git", - "reference": "7f1a2c5655be4c6e0d45574153bb5753c8afa4b7" + "reference": "ab6f9041b61ef517bc3e81891a4969181a39c17a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-ziparchive/zipball/7f1a2c5655be4c6e0d45574153bb5753c8afa4b7", - "reference": "7f1a2c5655be4c6e0d45574153bb5753c8afa4b7", + "url": "https://api.github.com/repos/thephpleague/flysystem-ziparchive/zipball/ab6f9041b61ef517bc3e81891a4969181a39c17a", + "reference": "ab6f9041b61ef517bc3e81891a4969181a39c17a", "shasum": "" }, "require": { @@ -3465,9 +3535,9 @@ "zip" ], "support": { - "source": "https://github.com/thephpleague/flysystem-ziparchive/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem-ziparchive/tree/3.31.0" }, - "time": "2024-08-09T21:24:39+00:00" + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/mime-type-detection", @@ -3603,20 +3673,20 @@ }, { "name": "league/uri", - "version": "7.6.0", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "f625804987a0a9112d954f9209d91fec52182344" + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/f625804987a0a9112d954f9209d91fec52182344", - "reference": "f625804987a0a9112d954f9209d91fec52182344", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.6", + "league/uri-interfaces": "^7.8.1", "php": "^8.1", "psr/http-factory": "^1" }, @@ -3630,11 +3700,11 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "ext-uri": "to use the PHP native URI class", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", - "league/uri-components": "Needed to easily manipulate URI objects components", - "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle WHATWG URL", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3689,7 +3759,7 @@ "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.6.0" + "source": "https://github.com/thephpleague/uri/tree/7.8.1" }, "funding": [ { @@ -3697,20 +3767,20 @@ "type": "github" } ], - "time": "2025-11-18T12:17:23+00:00" + "time": "2026-03-15T20:22:25+00:00" }, { "name": "league/uri-interfaces", - "version": "7.6.0", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368" + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/ccbfb51c0445298e7e0b7f4481b942f589665368", - "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", "shasum": "" }, "require": { @@ -3723,7 +3793,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle WHATWG URL", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3773,7 +3843,7 @@ "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.6.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" }, "funding": [ { @@ -3781,20 +3851,20 @@ "type": "github" } ], - "time": "2025-11-18T12:17:23+00:00" + "time": "2026-03-08T20:05:35+00:00" }, { "name": "maennchen/zipstream-php", - "version": "3.2.0", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416" + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/9712d8fa4cdf9240380b01eb4be55ad8dcf71416", - "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", "shasum": "" }, "require": { @@ -3805,7 +3875,7 @@ "require-dev": { "brianium/paratest": "^7.7", "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.16", + "friendsofphp/php-cs-fixer": "^3.86", "guzzlehttp/guzzle": "^7.5", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.5", @@ -3851,7 +3921,7 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.0" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2" }, "funding": [ { @@ -3859,7 +3929,7 @@ "type": "github" } ], - "time": "2025-07-17T11:15:13+00:00" + "time": "2026-04-11T18:38:28+00:00" }, { "name": "maize-tech/laravel-markable", @@ -4022,29 +4092,28 @@ }, { "name": "mobiledetect/mobiledetectlib", - "version": "4.8.09", + "version": "4.11.0", "source": { "type": "git", "url": "https://github.com/serbanghita/Mobile-Detect.git", - "reference": "a06fe2e546a06bb8c2639d6823d5250b2efb3209" + "reference": "ab39168b7556f44c11c80be1222b44b239f5c2e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/a06fe2e546a06bb8c2639d6823d5250b2efb3209", - "reference": "a06fe2e546a06bb8c2639d6823d5250b2efb3209", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/ab39168b7556f44c11c80be1222b44b239f5c2e4", + "reference": "ab39168b7556f44c11c80be1222b44b239f5c2e4", "shasum": "" }, "require": { - "php": ">=8.0", - "psr/cache": "^3.0", - "psr/simple-cache": "^3" + "php": ">=8.2", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^v3.65.0", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.12.x-dev", - "phpunit/phpunit": "^9.6.18", - "squizlabs/php_codesniffer": "^3.11.1" + "friendsofphp/php-cs-fixer": "3.95.1", + "phpbench/phpbench": "1.6.1", + "phpstan/phpstan": "2.1.47", + "phpunit/phpunit": "9.6.34", + "squizlabs/php_codesniffer": "3.13.5" }, "type": "library", "autoload": { @@ -4075,7 +4144,7 @@ ], "support": { "issues": "https://github.com/serbanghita/Mobile-Detect/issues", - "source": "https://github.com/serbanghita/Mobile-Detect/tree/4.8.09" + "source": "https://github.com/serbanghita/Mobile-Detect/tree/4.11.0" }, "funding": [ { @@ -4083,20 +4152,20 @@ "type": "github" } ], - "time": "2024-12-10T15:32:06+00:00" + "time": "2026-05-24T12:32:40+00:00" }, { "name": "monolog/monolog", - "version": "3.9.0", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", "shasum": "" }, "require": { @@ -4114,7 +4183,7 @@ "graylog2/gelf-php": "^1.4.2 || ^2.0", "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", + "mongodb/mongodb": "^1.8 || ^2.0", "php-amqplib/php-amqplib": "~2.4 || ^3", "php-console/php-console": "^3.1.8", "phpstan/phpstan": "^2", @@ -4174,7 +4243,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" }, "funding": [ { @@ -4186,30 +4255,30 @@ "type": "tidelift" } ], - "time": "2025-03-24T10:02:05+00:00" + "time": "2026-01-02T08:56:05+00:00" }, { "name": "mpociot/versionable", - "version": "4.4.5", + "version": "4.4.8", "source": { "type": "git", "url": "https://github.com/mpociot/versionable.git", - "reference": "25b1c606a319796f8e1023131d5529856cd84042" + "reference": "dae1d93e615fbc36963d34a53ae649e3411ae09b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mpociot/versionable/zipball/25b1c606a319796f8e1023131d5529856cd84042", - "reference": "25b1c606a319796f8e1023131d5529856cd84042", + "url": "https://api.github.com/repos/mpociot/versionable/zipball/dae1d93e615fbc36963d34a53ae649e3411ae09b", + "reference": "dae1d93e615fbc36963d34a53ae649e3411ae09b", "shasum": "" }, "require": { - "illuminate/support": "~5.3 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "illuminate/support": "~5.3 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0", "php": ">=7.1.0 || >=7.2.5 || >=8.0 || >=8.1 || >= 8.2" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^3.1 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.5.3" + "orchestra/testbench": "^3.1 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.5.3 || ^12.5.12" }, "type": "library", "extra": { @@ -4252,7 +4321,7 @@ "issues": "https://github.com/mpociot/versionable/issues", "source": "https://github.com/mpociot/versionable" }, - "time": "2025-02-25T10:43:28+00:00" + "time": "2026-04-08T10:27:32+00:00" }, { "name": "mtdowling/jmespath.php", @@ -4322,16 +4391,16 @@ }, { "name": "nesbot/carbon", - "version": "3.11.0", + "version": "3.11.4", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "bdb375400dcd162624531666db4799b36b64e4a1" + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1", - "reference": "bdb375400dcd162624531666db4799b36b64e4a1", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60", + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60", "shasum": "" }, "require": { @@ -4355,7 +4424,7 @@ "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^2.1.22", "phpunit/phpunit": "^10.5.53", - "squizlabs/php_codesniffer": "^3.13.4" + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" }, "bin": [ "bin/carbon" @@ -4398,14 +4467,14 @@ } ], "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbon.nesbot.com", + "homepage": "https://carbonphp.github.io/carbon/", "keywords": [ "date", "datetime", "time" ], "support": { - "docs": "https://carbon.nesbot.com/docs", + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", "issues": "https://github.com/CarbonPHP/carbon/issues", "source": "https://github.com/CarbonPHP/carbon" }, @@ -4423,20 +4492,20 @@ "type": "tidelift" } ], - "time": "2025-12-02T21:04:28+00:00" + "time": "2026-04-07T09:57:54+00:00" }, { "name": "nette/schema", - "version": "v1.3.3", + "version": "v1.3.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", - "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", "shasum": "" }, "require": { @@ -4444,8 +4513,10 @@ "php": "8.1 - 8.5" }, "require-dev": { - "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^2.0@stable", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -4486,22 +4557,22 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.3" + "source": "https://github.com/nette/schema/tree/v1.3.5" }, - "time": "2025-10-30T22:57:59+00:00" + "time": "2026-02-23T03:47:12+00:00" }, { "name": "nette/utils", - "version": "v4.1.0", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0" + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", - "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", + "url": "https://api.github.com/repos/nette/utils/zipball/7da6c396d7ebe142bc857c20479d5e70a5e1aac7", + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7", "shasum": "" }, "require": { @@ -4513,8 +4584,10 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", "nette/tester": "^2.5", - "phpstan/phpstan-nette": "^2.0@stable", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -4575,22 +4648,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.1.0" + "source": "https://github.com/nette/utils/tree/v4.1.4" }, - "time": "2025-12-01T17:49:23+00:00" + "time": "2026-05-11T20:49:54+00:00" }, { "name": "nikic/php-parser", - "version": "v5.6.2", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -4633,37 +4706,37 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-10-21T19:32:17+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.3.3", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017" + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017", - "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.3.6" + "symfony/console": "^7.4.4 || ^8.0.4" }, "require-dev": { - "illuminate/console": "^11.46.1", - "laravel/pint": "^1.25.1", + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", "phpstan/phpstan": "^1.12.32", "phpstan/phpstan-strict-rules": "^1.6.2", - "symfony/var-dumper": "^7.3.5", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -4695,7 +4768,7 @@ "email": "enunomaduro@gmail.com" } ], - "description": "Its like Tailwind CSS, but for the console.", + "description": "It's like Tailwind CSS, but for the console.", "keywords": [ "cli", "console", @@ -4706,7 +4779,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.3" + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" }, "funding": [ { @@ -4722,33 +4795,33 @@ "type": "github" } ], - "time": "2025-11-20T02:34:59+00:00" + "time": "2026-02-16T23:10:27+00:00" }, { "name": "owen-it/laravel-auditing", - "version": "v14.0.0", + "version": "v14.0.3", "source": { "type": "git", "url": "https://github.com/owen-it/laravel-auditing.git", - "reference": "f92602d1b3f53df29ddd577290e9d735ea707c53" + "reference": "34e8a21890082a7a353894a4acdeb2d301dbe0d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/owen-it/laravel-auditing/zipball/f92602d1b3f53df29ddd577290e9d735ea707c53", - "reference": "f92602d1b3f53df29ddd577290e9d735ea707c53", + "url": "https://api.github.com/repos/owen-it/laravel-auditing/zipball/34e8a21890082a7a353894a4acdeb2d301dbe0d4", + "reference": "34e8a21890082a7a353894a4acdeb2d301dbe0d4", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/console": "^11.0|^12.0", - "illuminate/database": "^11.0|^12.0", - "illuminate/filesystem": "^11.0|^12.0", + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/database": "^11.0|^12.0|^13.0", + "illuminate/filesystem": "^11.0|^12.0|^13.0", "php": "^8.2" }, "require-dev": { "mockery/mockery": "^1.5.1", - "orchestra/testbench": "^9.0|^10.0", - "phpunit/phpunit": "^11.0" + "orchestra/testbench": "^9.0|^10.0|^11.0", + "phpunit/phpunit": "^11.0|^12.5.12" }, "type": "package", "extra": { @@ -4806,7 +4879,7 @@ "issues": "https://github.com/owen-it/laravel-auditing/issues", "source": "https://github.com/owen-it/laravel-auditing" }, - "time": "2025-02-26T16:40:54+00:00" + "time": "2026-03-27T13:27:17+00:00" }, { "name": "paragonie/constant_time_encoding", @@ -4928,148 +5001,553 @@ "time": "2020-10-15T08:29:30+00:00" }, { - "name": "php-http/discovery", - "version": "1.20.0", + "name": "pear/archive_tar", + "version": "1.6.0", "source": { "type": "git", - "url": "https://github.com/php-http/discovery.git", - "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" + "url": "https://github.com/pear/Archive_Tar.git", + "reference": "dc3285537f1832da8ddbbe45f5a007248b6cc00e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", - "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/dc3285537f1832da8ddbbe45f5a007248b6cc00e", + "reference": "dc3285537f1832da8ddbbe45f5a007248b6cc00e", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "nyholm/psr7": "<1.0", - "zendframework/zend-diactoros": "*" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "*", - "psr/http-factory-implementation": "*", - "psr/http-message-implementation": "*" + "pear/pear-core-minimal": "^1.10.0alpha2", + "php": ">=5.4.0" }, "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "graham-campbell/phpspec-skip-example-extension": "^5.0", - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0", - "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", - "sebastian/comparator": "^3.0.5 || ^4.0.8", - "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + "phpunit/phpunit": "*" }, - "type": "composer-plugin", + "suggest": { + "ext-bz2": "Bz2 compression support.", + "ext-xz": "Lzma2 compression support.", + "ext-zlib": "Gzip compression support." + }, + "type": "library", "extra": { - "class": "Http\\Discovery\\Composer\\Plugin", - "plugin-optional": true + "branch-alias": { + "dev-master": "1.4.x-dev" + } }, "autoload": { - "psr-4": { - "Http\\Discovery\\": "src/" - }, - "exclude-from-classmap": [ - "src/Composer/Plugin.php" - ] + "psr-0": { + "Archive_Tar": "" + } }, "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], "license": [ - "MIT" + "BSD-2-Clause" ], "authors": [ { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" + "name": "Vincent Blavet", + "email": "vincent@phpconcept.net" + }, + { + "name": "Greg Beaver", + "email": "greg@chiaraquartet.net" + }, + { + "name": "Michiel Rook", + "email": "mrook@php.net" } ], - "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", - "homepage": "http://php-http.org", + "description": "Tar file management class with compression support (gzip, bzip2, lzma2)", + "homepage": "https://github.com/pear/Archive_Tar", "keywords": [ - "adapter", - "client", - "discovery", - "factory", - "http", - "message", - "psr17", - "psr7" + "archive", + "tar" ], "support": { - "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.20.0" + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar", + "source": "https://github.com/pear/Archive_Tar" }, - "time": "2024-10-02T11:20:13+00:00" + "time": "2025-07-19T14:49:16+00:00" }, { - "name": "phpoption/phpoption", - "version": "1.9.4", + "name": "pear/console_getopt", + "version": "v1.4.3", "source": { "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + "url": "https://github.com/pear/Console_Getopt.git", + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", - "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/a41f8d3e668987609178c7c4a9fe48fecac53fa0", + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0", "shasum": "" }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" - }, "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "1.9-dev" - } - }, "autoload": { - "psr-4": { - "PhpOption\\": "src/PhpOption/" + "psr-0": { + "Console": "./" } }, "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], "license": [ - "Apache-2.0" + "BSD-2-Clause" ], "authors": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh" + "name": "Andrei Zmievski", + "email": "andrei@php.net", + "role": "Lead" }, { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" + "name": "Stig Bakken", + "email": "stig@php.net", + "role": "Developer" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net", + "role": "Helper" } ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], + "description": "More info available on: http://pear.php.net/package/Console_Getopt", "support": { - "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Console_Getopt", + "source": "https://github.com/pear/Console_Getopt" }, - "funding": [ + "time": "2019-11-20T18:27:48+00:00" + }, + { + "name": "pear/pear-core-minimal", + "version": "v1.10.18", + "source": { + "type": "git", + "url": "https://github.com/pear/pear-core-minimal.git", + "reference": "c7b55789d01de0ce090d289b73f1bbd6a2f113b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/c7b55789d01de0ce090d289b73f1bbd6a2f113b1", + "reference": "c7b55789d01de0ce090d289b73f1bbd6a2f113b1", + "shasum": "" + }, + "require": { + "pear/console_getopt": "~1.4", + "pear/pear_exception": "~1.0", + "php": ">=5.4" + }, + "replace": { + "rsky/pear-core-min": "self.version" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "src/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@php.net", + "role": "Lead" + } + ], + "description": "Minimal set of PEAR core files to be used as composer dependency", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR", + "source": "https://github.com/pear/pear-core-minimal" + }, + "time": "2025-12-14T20:37:07+00:00" + }, + { + "name": "pear/pear_exception", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/pear/PEAR_Exception.git", + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "<9" + }, + "type": "class", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "PEAR/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "." + ], + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Helgi Thormar", + "email": "dufuz@php.net" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net" + } + ], + "description": "The PEAR Exception base class.", + "homepage": "https://github.com/pear/PEAR_Exception", + "keywords": [ + "exception" + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR_Exception", + "source": "https://github.com/pear/PEAR_Exception" + }, + "time": "2021-03-21T15:43:46+00:00" + }, + { + "name": "php-http/discovery", + "version": "1.20.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.20.0" + }, + "time": "2024-10-02T11:20:13+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" + }, + "time": "2026-03-18T20:49:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" + }, + "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" @@ -5079,20 +5557,20 @@ "type": "tidelift" } ], - "time": "2025-08-21T11:53:16+00:00" + "time": "2025-12-27T19:41:33+00:00" }, { "name": "phpseclib/phpseclib", - "version": "3.0.47", + "version": "3.0.52", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" + "reference": "2adaefc83df2ec548558307690f376dd7d4f4fce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", - "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/2adaefc83df2ec548558307690f376dd7d4f4fce", + "reference": "2adaefc83df2ec548558307690f376dd7d4f4fce", "shasum": "" }, "require": { @@ -5173,55 +5651,102 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.52" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2026-04-27T07:02:15+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" }, - "funding": [ - { - "url": "https://github.com/terrafrost", - "type": "github" - }, - { - "url": "https://www.patreon.com/phpseclib", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", - "type": "tidelift" + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "time": "2025-10-06T01:07:24+00:00" + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + }, + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "11.0.11", + "version": "11.0.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", - "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.4.0", + "nikic/php-parser": "^5.7.0", "php": ">=8.2", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-text-template": "^4.0.1", "sebastian/code-unit-reverse-lookup": "^4.0.1", "sebastian/complexity": "^4.0.1", - "sebastian/environment": "^7.2.0", + "sebastian/environment": "^7.2.1", "sebastian/lines-of-code": "^3.0.1", "sebastian/version": "^5.0.2", - "theseer/tokenizer": "^1.2.3" + "theseer/tokenizer": "^1.3.1" }, "require-dev": { - "phpunit/phpunit": "^11.5.2" + "phpunit/phpunit": "^11.5.46" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -5259,7 +5784,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/11.0.11" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" }, "funding": [ { @@ -5279,32 +5804,32 @@ "type": "tidelift" } ], - "time": "2025-08-27T14:37:49+00:00" + "time": "2025-12-24T07:01:01+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -5332,15 +5857,27 @@ "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/5.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2024-08-27T05:02:59+00:00" + "time": "2026-02-02T13:52:54+00:00" }, { "name": "phpunit/php-text-template", @@ -5979,16 +6516,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.15", + "version": "v0.12.23", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c" + "reference": "4dcc0f08047d52bbde475eda481146fd8e27e1a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/38953bc71491c838fcb6ebcbdc41ab7483cd549c", - "reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4dcc0f08047d52bbde475eda481146fd8e27e1a4", + "reference": "4dcc0f08047d52bbde475eda481146fd8e27e1a4", "shasum": "" }, "require": { @@ -5996,8 +6533,8 @@ "ext-tokenizer": "*", "nikic/php-parser": "^5.0 || ^4.0", "php": "^8.0 || ^7.4", - "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" }, "conflict": { "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" @@ -6052,9 +6589,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.15" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.23" }, - "time": "2025-11-28T00:00:14+00:00" + "time": "2026-05-23T13:41:31+00:00" }, { "name": "ralouphie/getallheaders", @@ -6178,20 +6715,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.1", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" + "reference": "8429c78ca35a09f27565311b98101e2826affde0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", - "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -6250,9 +6787,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.1" + "source": "https://github.com/ramsey/uuid/tree/4.9.2" }, - "time": "2025-09-04T20:59:21+00:00" + "time": "2025-12-14T04:43:48+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -6558,22 +7095,22 @@ }, { "name": "socialiteproviders/manager", - "version": "v4.8.1", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/SocialiteProviders/Manager.git", - "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4" + "reference": "35372dc62787e61e91cfec73f45fd5d5ae0f8891" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/8180ec14bef230ec2351cff993d5d2d7ca470ef4", - "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4", + "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/35372dc62787e61e91cfec73f45fd5d5ae0f8891", + "reference": "35372dc62787e61e91cfec73f45fd5d5ae0f8891", "shasum": "" }, "require": { - "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "illuminate/support": "^11.0 || ^12.0 || ^13.0", "laravel/socialite": "^5.5", - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "mockery/mockery": "^1.2", @@ -6628,70 +7165,20 @@ "issues": "https://github.com/socialiteproviders/manager/issues", "source": "https://github.com/socialiteproviders/manager" }, - "time": "2025-02-24T19:33:30+00:00" - }, - { - "name": "socialiteproviders/orcid", - "version": "5.1.0", - "source": { - "type": "git", - "url": "https://github.com/SocialiteProviders/Orcid.git", - "reference": "9a61194bd23394b851ef6d879991ebc284cd74d8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/SocialiteProviders/Orcid/zipball/9a61194bd23394b851ef6d879991ebc284cd74d8", - "reference": "9a61194bd23394b851ef6d879991ebc284cd74d8", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^8.0", - "socialiteproviders/manager": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "SocialiteProviders\\Orcid\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ben Cornwell", - "email": "ben@bencornwell.com" - } - ], - "description": "ORCID OAuth2 Provider for Laravel Socialite", - "keywords": [ - "laravel", - "oauth", - "orcid", - "provider", - "socialite" - ], - "support": { - "docs": "https://socialiteproviders.com/orcid", - "issues": "https://github.com/socialiteproviders/providers/issues", - "source": "https://github.com/socialiteproviders/providers" - }, - "time": "2024-02-01T18:57:00+00:00" + "time": "2026-03-18T22:13:24+00:00" }, { "name": "spatie/db-dumper", - "version": "3.8.1", + "version": "3.8.3", "source": { "type": "git", "url": "https://github.com/spatie/db-dumper.git", - "reference": "e974cc7862b8de1bd3b7ea7d4839ba7167acb546" + "reference": "eac3221fbe27fac51f388600d27b67b1b079406e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/db-dumper/zipball/e974cc7862b8de1bd3b7ea7d4839ba7167acb546", - "reference": "e974cc7862b8de1bd3b7ea7d4839ba7167acb546", + "url": "https://api.github.com/repos/spatie/db-dumper/zipball/eac3221fbe27fac51f388600d27b67b1b079406e", + "reference": "eac3221fbe27fac51f388600d27b67b1b079406e", "shasum": "" }, "require": { @@ -6729,7 +7216,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/db-dumper/tree/3.8.1" + "source": "https://github.com/spatie/db-dumper/tree/3.8.3" }, "funding": [ { @@ -6741,32 +7228,32 @@ "type": "github" } ], - "time": "2025-11-26T09:51:23+00:00" + "time": "2026-01-05T16:26:03+00:00" }, { "name": "spatie/eloquent-sortable", - "version": "4.5.2", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/spatie/eloquent-sortable.git", - "reference": "c1c4f3a66cd41eb7458783c8a4c8e5d7924a9f20" + "reference": "caf2596e5df0260d0e2863e89b750611eef2fc59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/eloquent-sortable/zipball/c1c4f3a66cd41eb7458783c8a4c8e5d7924a9f20", - "reference": "c1c4f3a66cd41eb7458783c8a4c8e5d7924a9f20", + "url": "https://api.github.com/repos/spatie/eloquent-sortable/zipball/caf2596e5df0260d0e2863e89b750611eef2fc59", + "reference": "caf2596e5df0260d0e2863e89b750611eef2fc59", "shasum": "" }, "require": { - "illuminate/database": "^9.31|^10.0|^11.0|^12.0", - "illuminate/support": "^9.31|^10.0|^11.0|^12.0", + "illuminate/database": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "nesbot/carbon": "^2.63|^3.0", - "php": "^8.1", + "php": "^8.2", "spatie/laravel-package-tools": "^1.9" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", - "phpunit/phpunit": "^9.5|^10.0|^11.5.3" + "orchestra/testbench": "^8.0|^9.6|^10.0|^11.0", + "phpunit/phpunit": "^10.0|^11.5.3|^12.0" }, "type": "library", "extra": { @@ -6803,7 +7290,7 @@ ], "support": { "issues": "https://github.com/spatie/eloquent-sortable/issues", - "source": "https://github.com/spatie/eloquent-sortable/tree/4.5.2" + "source": "https://github.com/spatie/eloquent-sortable/tree/5.0.1" }, "funding": [ { @@ -6815,20 +7302,20 @@ "type": "github" } ], - "time": "2025-08-25T11:46:57+00:00" + "time": "2026-02-21T21:26:43+00:00" }, { "name": "spatie/laravel-backup", - "version": "9.3.7", + "version": "9.4.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-backup.git", - "reference": "6aa2d0ef42218ba6c1f627a17ade3e1ffd0e18af" + "reference": "0dff805039617b4a6f48291cef0697a73bc0392e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/6aa2d0ef42218ba6c1f627a17ade3e1ffd0e18af", - "reference": "6aa2d0ef42218ba6c1f627a17ade3e1ffd0e18af", + "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/0dff805039617b4a6f48291cef0697a73bc0392e", + "reference": "0dff805039617b4a6f48291cef0697a73bc0392e", "shasum": "" }, "require": { @@ -6903,7 +7390,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-backup/issues", - "source": "https://github.com/spatie/laravel-backup/tree/9.3.7" + "source": "https://github.com/spatie/laravel-backup/tree/9.4.1" }, "funding": [ { @@ -6915,33 +7402,33 @@ "type": "other" } ], - "time": "2025-11-26T15:43:43+00:00" + "time": "2026-02-15T19:05:20+00:00" }, { "name": "spatie/laravel-cookie-consent", - "version": "3.3.3", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-cookie-consent.git", - "reference": "fa375d4c9cae8745137ec3249e1b8e35d911963a" + "reference": "67a7717c2ed43fa27dd24a5df2a71272b47937b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-cookie-consent/zipball/fa375d4c9cae8745137ec3249e1b8e35d911963a", - "reference": "fa375d4c9cae8745137ec3249e1b8e35d911963a", + "url": "https://api.github.com/repos/spatie/laravel-cookie-consent/zipball/67a7717c2ed43fa27dd24a5df2a71272b47937b4", + "reference": "67a7717c2ed43fa27dd24a5df2a71272b47937b4", "shasum": "" }, "require": { - "illuminate/cookie": "^10.0|^11.0|^12.0", - "illuminate/support": "^10.0|^11.0|^12.0", - "illuminate/view": "^10.0|^11.0|^12.0", + "illuminate/cookie": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "illuminate/view": "^11.0|^12.0|^13.0", "php": "^8.2", "spatie/laravel-package-tools": "^1.9" }, "require-dev": { "fakerphp/faker": "^1.9", - "orchestra/testbench": "^8.0|^9.0|^10.0", - "pestphp/pest": "^2.34|^3.7" + "orchestra/testbench": "^9.0|^10.0|^11.0", + "pestphp/pest": "^3.7|^4.0" }, "type": "library", "extra": { @@ -6985,7 +7472,93 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-cookie-consent/tree/3.3.3" + "source": "https://github.com/spatie/laravel-cookie-consent/tree/3.4.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2026-02-21T15:58:52+00:00" + }, + { + "name": "spatie/laravel-csp", + "version": "3.25.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-csp.git", + "reference": "f7ea0977be9d481b6f03a58956313f516971b5b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-csp/zipball/f7ea0977be9d481b6f03a58956313f516971b5b4", + "reference": "f7ea0977be9d481b6f03a58956313f516971b5b4", + "shasum": "" + }, + "require": { + "illuminate/http": "^11.36.1|^12.0|^13.0", + "illuminate/support": "^11.36.1|^12.0|^13.0", + "php": "^8.3", + "spatie/laravel-package-tools": "^1.17" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.9|^10.0|^11.0", + "pestphp/pest": "^3.0|^4.0", + "roave/security-advisories": "dev-master" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Csp\\CspServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\Csp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Verhelst", + "email": "tvke91@gmail.com", + "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" + } + ], + "description": "Add CSP headers to the responses of a Laravel app", + "homepage": "https://github.com/spatie/laravel-csp", + "keywords": [ + "content-security-policy", + "csp", + "headers", + "laravel", + "laravel-csp", + "security", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-csp/issues", + "source": "https://github.com/spatie/laravel-csp/tree/3.25.0" }, "funding": [ { @@ -6993,36 +7566,36 @@ "type": "custom" } ], - "time": "2025-02-21T13:11:14+00:00" + "time": "2026-05-11T08:02:16+00:00" }, { "name": "spatie/laravel-honeypot", - "version": "4.6.2", + "version": "4.7.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-honeypot.git", - "reference": "62ec9dbecd2a17a4e2af62b09675f89813295cac" + "reference": "79802de082f05b13b82258c392e294a81d31e0cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-honeypot/zipball/62ec9dbecd2a17a4e2af62b09675f89813295cac", - "reference": "62ec9dbecd2a17a4e2af62b09675f89813295cac", + "url": "https://api.github.com/repos/spatie/laravel-honeypot/zipball/79802de082f05b13b82258c392e294a81d31e0cd", + "reference": "79802de082f05b13b82258c392e294a81d31e0cd", "shasum": "" }, "require": { - "illuminate/contracts": "^11.0|^12.0", - "illuminate/encryption": "^11.0|^12.0", - "illuminate/http": "^11.0|^12.0", - "illuminate/support": "^11.0|^12.0", - "illuminate/validation": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0|^13.0", + "illuminate/encryption": "^11.0|^12.0|^13.0", + "illuminate/http": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "illuminate/validation": "^11.0|^12.0|^13.0", "nesbot/carbon": "^2.0|^3.0", "php": "^8.2", "spatie/laravel-package-tools": "^1.9", "symfony/http-foundation": "^7.0|^8.0" }, "require-dev": { - "livewire/livewire": "^3.0", - "orchestra/testbench": "^9.0|^10.0", + "livewire/livewire": "^3.0|^4.0", + "orchestra/testbench": "^9.0|^10.0|^11.0", "pestphp/pest": "^2.0|^3.0|^4.0", "pestphp/pest-plugin-livewire": "^1.0|^2.1|^3.0|^4.0", "spatie/pest-plugin-snapshots": "^1.1|^2.1", @@ -7061,7 +7634,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-honeypot/tree/4.6.2" + "source": "https://github.com/spatie/laravel-honeypot/tree/4.7.1" }, "funding": [ { @@ -7069,33 +7642,33 @@ "type": "custom" } ], - "time": "2025-11-28T09:57:48+00:00" + "time": "2026-02-27T14:58:09+00:00" }, { "name": "spatie/laravel-package-tools", - "version": "1.92.7", + "version": "1.93.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "f09a799850b1ed765103a4f0b4355006360c49a5" + "reference": "d5552849801f2642aea710557463234b59ef65eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/f09a799850b1ed765103a4f0b4355006360c49a5", - "reference": "f09a799850b1ed765103a4f0b4355006360c49a5", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/d5552849801f2642aea710557463234b59ef65eb", + "reference": "d5552849801f2642aea710557463234b59ef65eb", "shasum": "" }, "require": { - "illuminate/contracts": "^9.28|^10.0|^11.0|^12.0", - "php": "^8.0" + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.1" }, "require-dev": { "mockery/mockery": "^1.5", - "orchestra/testbench": "^7.7|^8.0|^9.0|^10.0", - "pestphp/pest": "^1.23|^2.1|^3.1", - "phpunit/php-code-coverage": "^9.0|^10.0|^11.0", - "phpunit/phpunit": "^9.5.24|^10.5|^11.5", - "spatie/pest-plugin-test-time": "^1.1|^2.2" + "orchestra/testbench": "^8.0|^9.2|^10.0|^11.0", + "pestphp/pest": "^2.1|^3.1|^4.0", + "phpunit/php-code-coverage": "^10.0|^11.0|^12.0", + "phpunit/phpunit": "^10.5|^11.5|^12.5", + "spatie/pest-plugin-test-time": "^2.2|^3.0" }, "type": "library", "autoload": { @@ -7122,7 +7695,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.92.7" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.93.1" }, "funding": [ { @@ -7130,34 +7703,35 @@ "type": "github" } ], - "time": "2025-07-17T15:46:43+00:00" + "time": "2026-05-19T14:06:37+00:00" }, { "name": "spatie/laravel-permission", - "version": "6.23.0", + "version": "6.25.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-permission.git", - "reference": "9e41247bd512b1e6c229afbc1eb528f7565ae3bb" + "reference": "d7d4cb0d58616722f1afc90e0484e4825155b9b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/9e41247bd512b1e6c229afbc1eb528f7565ae3bb", - "reference": "9e41247bd512b1e6c229afbc1eb528f7565ae3bb", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/d7d4cb0d58616722f1afc90e0484e4825155b9b3", + "reference": "d7d4cb0d58616722f1afc90e0484e4825155b9b3", "shasum": "" }, "require": { - "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", - "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", - "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", - "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^8.0" }, "require-dev": { - "laravel/passport": "^11.0|^12.0", + "laravel/passport": "^11.0|^12.0|^13.0", "laravel/pint": "^1.0", - "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0", - "phpunit/phpunit": "^9.4|^10.1|^11.5" + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.0|^3.0|^4.0", + "pestphp/pest-plugin-laravel": "^2.0|^3.0|^4.0" }, "type": "library", "extra": { @@ -7205,7 +7779,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-permission/issues", - "source": "https://github.com/spatie/laravel-permission/tree/6.23.0" + "source": "https://github.com/spatie/laravel-permission/tree/6.25.0" }, "funding": [ { @@ -7213,26 +7787,26 @@ "type": "github" } ], - "time": "2025-11-03T20:16:13+00:00" + "time": "2026-03-17T22:46:46+00:00" }, { "name": "spatie/laravel-query-builder", - "version": "6.3.6", + "version": "6.4.4", "source": { "type": "git", "url": "https://github.com/spatie/laravel-query-builder.git", - "reference": "b9a5af31c79ae8fb79ae0aa8ba2b9e7180f39481" + "reference": "ab9c4c369fc913d6c020a0f6776f3c82f3e523fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/b9a5af31c79ae8fb79ae0aa8ba2b9e7180f39481", - "reference": "b9a5af31c79ae8fb79ae0aa8ba2b9e7180f39481", + "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/ab9c4c369fc913d6c020a0f6776f3c82f3e523fb", + "reference": "ab9c4c369fc913d6c020a0f6776f3c82f3e523fb", "shasum": "" }, "require": { - "illuminate/database": "^10.0|^11.0|^12.0", - "illuminate/http": "^10.0|^11.0|^12.0", - "illuminate/support": "^10.0|^11.0|^12.0", + "illuminate/database": "^10.0|^11.0|^12.0|^13.0", + "illuminate/http": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "php": "^8.2", "spatie/laravel-package-tools": "^1.11" }, @@ -7240,7 +7814,7 @@ "ext-json": "*", "larastan/larastan": "^2.7 || ^3.3", "mockery/mockery": "^1.4", - "orchestra/testbench": "^7.0|^8.0|^10.0", + "orchestra/testbench": "^7.0|^8.0|^10.0|^11.0", "pestphp/pest": "^2.0|^3.7|^4.0", "phpunit/phpunit": "^10.0|^11.5.3|^12.0", "spatie/invade": "^2.0" @@ -7287,24 +7861,24 @@ "type": "custom" } ], - "time": "2025-10-20T09:50:21+00:00" + "time": "2026-03-08T13:45:05+00:00" }, { "name": "spatie/laravel-signal-aware-command", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-signal-aware-command.git", - "reference": "70207ba2702a9ee8f523af0cd4711d1104c5ca53" + "reference": "54dcc1efd152bfb3eb0faf56a5fc28569b864b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-signal-aware-command/zipball/70207ba2702a9ee8f523af0cd4711d1104c5ca53", - "reference": "70207ba2702a9ee8f523af0cd4711d1104c5ca53", + "url": "https://api.github.com/repos/spatie/laravel-signal-aware-command/zipball/54dcc1efd152bfb3eb0faf56a5fc28569b864b5d", + "reference": "54dcc1efd152bfb3eb0faf56a5fc28569b864b5d", "shasum": "" }, "require": { - "illuminate/contracts": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0|^13.0", "php": "^8.2", "spatie/laravel-package-tools": "^1.4.3", "symfony/console": "^7.0|^8.0" @@ -7354,7 +7928,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-signal-aware-command/issues", - "source": "https://github.com/spatie/laravel-signal-aware-command/tree/2.1.1" + "source": "https://github.com/spatie/laravel-signal-aware-command/tree/2.1.2" }, "funding": [ { @@ -7362,37 +7936,37 @@ "type": "github" } ], - "time": "2025-11-27T09:17:52+00:00" + "time": "2026-02-22T08:16:31+00:00" }, { "name": "spatie/laravel-support-bubble", - "version": "1.9.0", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-support-bubble.git", - "reference": "0de161473bdfd652bca9e9e9bbaefdcc51cc21e7" + "reference": "a765ec7511464c520f12331f213140f3d14d9e19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-support-bubble/zipball/0de161473bdfd652bca9e9e9bbaefdcc51cc21e7", - "reference": "0de161473bdfd652bca9e9e9bbaefdcc51cc21e7", + "url": "https://api.github.com/repos/spatie/laravel-support-bubble/zipball/a765ec7511464c520f12331f213140f3d14d9e19", + "reference": "a765ec7511464c520f12331f213140f3d14d9e19", "shasum": "" }, "require": { - "illuminate/contracts": "^10.0|^11.0|^12.0", + "illuminate/contracts": "^12.0|^13.0", "php": "^8.2", "spatie/laravel-honeypot": "^4.0", "spatie/laravel-package-tools": "^1.9" }, "require-dev": { - "brianium/paratest": "^6.2|^7.4", - "nunomaduro/collision": "^7.0|^8.0", - "orchestra/testbench": "^8.0|^9.0|^10.0", - "pestphp/pest": "^2.34|^3.7", - "pestphp/pest-plugin-laravel": "^2.3|^3.1", - "phpunit/phpunit": "^10.5|^11.5.3", + "brianium/paratest": "^7.4", + "nunomaduro/collision": "^8.0", + "orchestra/testbench": "^10.0|^11.0", + "pestphp/pest": "^3.7|^4.4", + "pestphp/pest-plugin-laravel": "^3.1|^4.1", + "phpunit/phpunit": "^11.5.3|^12.5.12", "spatie/laravel-ray": "^1.23", - "spatie/pest-plugin-snapshots": "^1.1|^2.1" + "spatie/pest-plugin-snapshots": "^2.1" }, "type": "library", "extra": { @@ -7438,7 +8012,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-support-bubble/issues", - "source": "https://github.com/spatie/laravel-support-bubble/tree/1.9.0" + "source": "https://github.com/spatie/laravel-support-bubble/tree/1.9.2" }, "funding": [ { @@ -7446,34 +8020,34 @@ "type": "github" } ], - "time": "2025-08-21T15:45:21+00:00" + "time": "2026-02-26T23:20:53+00:00" }, { "name": "spatie/laravel-tags", - "version": "4.10.1", + "version": "4.11.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-tags.git", - "reference": "26fe2ad5490e65e2a3475c3fe8a4d9609934aa40" + "reference": "7d89470d49da7ee8acd8d23b236643329c168376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-tags/zipball/26fe2ad5490e65e2a3475c3fe8a4d9609934aa40", - "reference": "26fe2ad5490e65e2a3475c3fe8a4d9609934aa40", + "url": "https://api.github.com/repos/spatie/laravel-tags/zipball/7d89470d49da7ee8acd8d23b236643329c168376", + "reference": "7d89470d49da7ee8acd8d23b236643329c168376", "shasum": "" }, "require": { - "laravel/framework": "^10.0|^11.0|^12.0", + "laravel/framework": "^10.0|^11.0|^12.0|^13.0", "nesbot/carbon": "^2.63|^3.0", "php": "^8.1", - "spatie/eloquent-sortable": "^4.0", + "spatie/eloquent-sortable": "^4.0|^5.0", "spatie/laravel-package-tools": "^1.4", "spatie/laravel-translatable": "^6.0" }, "require-dev": { - "orchestra/testbench": "^8.0|^9.0|^10.0", - "pestphp/pest": "^1.22|^2.0", - "phpunit/phpunit": "^9.5.2" + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^1.22|^2.0|^4.0", + "phpunit/phpunit": "^9.5.2|^12.5.12" }, "type": "library", "extra": { @@ -7508,7 +8082,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-tags/issues", - "source": "https://github.com/spatie/laravel-tags/tree/4.10.1" + "source": "https://github.com/spatie/laravel-tags/tree/4.11.0" }, "funding": [ { @@ -7516,32 +8090,32 @@ "type": "github" } ], - "time": "2025-10-13T14:16:14+00:00" + "time": "2026-02-21T15:13:58+00:00" }, { "name": "spatie/laravel-translatable", - "version": "6.12.0", + "version": "6.14.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-translatable.git", - "reference": "8fc0c1dd5ab4013c27a28e5d5590f2ce849bd349" + "reference": "d120a925cf413b2427f886264bb6eb102ac23d42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-translatable/zipball/8fc0c1dd5ab4013c27a28e5d5590f2ce849bd349", - "reference": "8fc0c1dd5ab4013c27a28e5d5590f2ce849bd349", + "url": "https://api.github.com/repos/spatie/laravel-translatable/zipball/d120a925cf413b2427f886264bb6eb102ac23d42", + "reference": "d120a925cf413b2427f886264bb6eb102ac23d42", "shasum": "" }, "require": { - "illuminate/database": "^11.0|^12.0", - "illuminate/support": "^11.0|^12.0", + "illuminate/database": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", "php": "^8.3", - "spatie/laravel-package-tools": "^1.92.7" + "spatie/laravel-package-tools": "^1.93.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.90", "mockery/mockery": "^1.6.12", - "orchestra/testbench": "^9.0|^10.0", + "orchestra/testbench": "^9.0|^10.0|^11.0", "pestphp/pest": "^4.0.0" }, "type": "library", @@ -7591,7 +8165,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-translatable/issues", - "source": "https://github.com/spatie/laravel-translatable/tree/6.12.0" + "source": "https://github.com/spatie/laravel-translatable/tree/6.14.1" }, "funding": [ { @@ -7599,33 +8173,33 @@ "type": "github" } ], - "time": "2025-11-24T15:57:48+00:00" + "time": "2026-04-23T08:26:29+00:00" }, { "name": "spatie/laravel-welcome-notification", - "version": "2.4.4", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-welcome-notification.git", - "reference": "9ffd013e3187c83708a2b5636ff2ddcce02b8fa5" + "reference": "b8eda37bc2bf0a1b6d71a5c0bd9458ba00cfa5ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-welcome-notification/zipball/9ffd013e3187c83708a2b5636ff2ddcce02b8fa5", - "reference": "9ffd013e3187c83708a2b5636ff2ddcce02b8fa5", + "url": "https://api.github.com/repos/spatie/laravel-welcome-notification/zipball/b8eda37bc2bf0a1b6d71a5c0bd9458ba00cfa5ff", + "reference": "b8eda37bc2bf0a1b6d71a5c0bd9458ba00cfa5ff", "shasum": "" }, "require": { - "illuminate/auth": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/notifications": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/queue": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/auth": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/notifications": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/queue": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^7.3|^8.0" }, "require-dev": { - "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", - "pestphp/pest": "^1.22|^2.34|^3.7", - "pestphp/pest-plugin-laravel": "^1.3|^2.3|^3.1", - "phpunit/phpunit": "^9.0|^10.5|^11.5.3", + "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^1.22|^2.34|^3.7|^4.4", + "pestphp/pest-plugin-laravel": "^1.3|^2.3|^3.1|^4.1", + "phpunit/phpunit": "^9.0|^10.5|^11.5.3|^12.5.12", "spatie/test-time": "^1.1" }, "type": "library", @@ -7661,7 +8235,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-welcome-notification/issues", - "source": "https://github.com/spatie/laravel-welcome-notification/tree/2.4.4" + "source": "https://github.com/spatie/laravel-welcome-notification/tree/2.5.0" }, "funding": [ { @@ -7669,20 +8243,20 @@ "type": "github" } ], - "time": "2025-02-26T08:58:35+00:00" + "time": "2026-02-22T18:44:58+00:00" }, { "name": "spatie/schema-org", - "version": "3.23.1", + "version": "3.23.0", "source": { "type": "git", "url": "https://github.com/spatie/schema-org.git", - "reference": "a8dc1b6fcdd06afc1ab084c3ead9b7a4c3d7a35d" + "reference": "c95abf7da59f16570aec18ca11d861255e645c39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/schema-org/zipball/a8dc1b6fcdd06afc1ab084c3ead9b7a4c3d7a35d", - "reference": "a8dc1b6fcdd06afc1ab084c3ead9b7a4c3d7a35d", + "url": "https://api.github.com/repos/spatie/schema-org/zipball/c95abf7da59f16570aec18ca11d861255e645c39", + "reference": "c95abf7da59f16570aec18ca11d861255e645c39", "shasum": "" }, "require": { @@ -7691,7 +8265,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.6", - "graham-campbell/analyzer": "^4.2", + "graham-campbell/analyzer": "^3.0.5", "illuminate/collections": "^8.62.0", "league/flysystem": "^2.3.0 || ^3.0", "pestphp/pest": "^1.21", @@ -7730,7 +8304,7 @@ ], "support": { "issues": "https://github.com/spatie/schema-org/issues", - "source": "https://github.com/spatie/schema-org/tree/3.23.1" + "source": "https://github.com/spatie/schema-org/tree/3.23.0" }, "funding": [ { @@ -7742,20 +8316,20 @@ "type": "github" } ], - "time": "2025-01-31T14:54:12+00:00" + "time": "2024-01-11T14:58:44+00:00" }, { "name": "spatie/temporary-directory", - "version": "2.3.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/spatie/temporary-directory.git", - "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b" + "reference": "662e481d6ec07ef29fd05010433428851a42cd07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/580eddfe9a0a41a902cac6eeb8f066b42e65a32b", - "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b", + "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/662e481d6ec07ef29fd05010433428851a42cd07", + "reference": "662e481d6ec07ef29fd05010433428851a42cd07", "shasum": "" }, "require": { @@ -7776,47 +8350,228 @@ ], "authors": [ { - "name": "Alex Vanderbist", - "email": "alex@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily create, use and destroy temporary directories", + "homepage": "https://github.com/spatie/temporary-directory", + "keywords": [ + "php", + "spatie", + "temporary-directory" + ], + "support": { + "issues": "https://github.com/spatie/temporary-directory/issues", + "source": "https://github.com/spatie/temporary-directory/tree/2.3.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2026-01-12T07:42:22+00:00" + }, + { + "name": "spomky-labs/cbor-php", + "version": "3.2.3", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/cbor-php.git", + "reference": "dd6eb84e6d92f7b8bd0da56b4b4dd7235aed0c32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/dd6eb84e6d92f7b8bd0da56b4b4dd7235aed0c32", + "reference": "dd6eb84e6d92f7b8bd0da56b4b4dd7235aed0c32", + "shasum": "" + }, + "require": { + "brick/math": "^0.9|^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17", + "ext-mbstring": "*", + "php": ">=8.0" + }, + "require-dev": { + "ext-json": "*", + "roave/security-advisories": "dev-latest", + "symfony/error-handler": "^6.4|^7.1|^8.0", + "symfony/var-dumper": "^6.4|^7.1|^8.0" + }, + "suggest": { + "ext-bcmath": "GMP or BCMath extensions will drastically improve the library performance. BCMath extension needed to handle the Big Float and Decimal Fraction Tags", + "ext-gmp": "GMP or BCMath extensions will drastically improve the library performance" + }, + "type": "library", + "autoload": { + "psr-4": { + "CBOR\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/Spomky-Labs/cbor-php/contributors" + } + ], + "description": "CBOR Encoder/Decoder for PHP", + "keywords": [ + "Concise Binary Object Representation", + "RFC7049", + "cbor" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/cbor-php/issues", + "source": "https://github.com/Spomky-Labs/cbor-php/tree/3.2.3" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2026-04-01T12:15:20+00:00" + }, + { + "name": "spomky-labs/pki-framework", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/pki-framework.git", + "reference": "aa576cbd07128075bef97ac2f8af9854e67513d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/aa576cbd07128075bef97ac2f8af9854e67513d8", + "reference": "aa576cbd07128075bef97ac2f8af9854e67513d8", + "shasum": "" + }, + "require": { + "brick/math": "^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17", + "ext-mbstring": "*", + "php": ">=8.1", + "psr/clock": "^1.0" + }, + "require-dev": { + "ekino/phpstan-banned-code": "^1.0|^2.0|^3.0", + "ext-gmp": "*", + "ext-openssl": "*", + "infection/infection": "^0.28|^0.29|^0.31|^0.32", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.3|^2.0", + "phpstan/phpstan": "^1.8|^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", + "phpstan/phpstan-phpunit": "^1.1|^2.0", + "phpstan/phpstan-strict-rules": "^1.3|^2.0", + "phpunit/phpunit": "^10.1|^11.0|^12.0|^13.0", + "rector/rector": "^1.0|^2.0", + "roave/security-advisories": "dev-latest", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symplify/easy-coding-standard": "^12.0|^13.0" + }, + "suggest": { + "ext-bcmath": "For better performance (or GMP)", + "ext-gmp": "For better performance (or BCMath)", + "ext-openssl": "For OpenSSL based cyphering" + }, + "type": "library", + "autoload": { + "psr-4": { + "SpomkyLabs\\Pki\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joni Eskelinen", + "email": "jonieske@gmail.com", + "role": "Original developer" + }, + { + "name": "Florent Morselli", + "email": "florent.morselli@spomky-labs.com", + "role": "Spomky-Labs PKI Framework developer" } ], - "description": "Easily create, use and destroy temporary directories", - "homepage": "https://github.com/spatie/temporary-directory", + "description": "A PHP framework for managing Public Key Infrastructures. It comprises X.509 public key certificates, attribute certificates, certification requests and certification path validation.", + "homepage": "https://github.com/spomky-labs/pki-framework", "keywords": [ - "php", - "spatie", - "temporary-directory" + "DER", + "Private Key", + "ac", + "algorithm identifier", + "asn.1", + "asn1", + "attribute certificate", + "certificate", + "certification request", + "cryptography", + "csr", + "decrypt", + "ec", + "encrypt", + "pem", + "pkcs", + "public key", + "rsa", + "sign", + "signature", + "verify", + "x.509", + "x.690", + "x509", + "x690" ], "support": { - "issues": "https://github.com/spatie/temporary-directory/issues", - "source": "https://github.com/spatie/temporary-directory/tree/2.3.0" + "issues": "https://github.com/Spomky-Labs/pki-framework/issues", + "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.4.2" }, "funding": [ { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" + "url": "https://github.com/Spomky", + "type": "github" }, { - "url": "https://github.com/spatie", - "type": "github" + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" } ], - "time": "2025-01-13T13:04:43+00:00" + "time": "2026-03-23T22:56:56+00:00" }, { "name": "swagger-api/swagger-ui", - "version": "v5.30.3", + "version": "v5.32.6", "source": { "type": "git", "url": "https://github.com/swagger-api/swagger-ui.git", - "reference": "199761a94d03753ec62c23bb4ba162bb73c3cfc7" + "reference": "dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/199761a94d03753ec62c23bb4ba162bb73c3cfc7", - "reference": "199761a94d03753ec62c23bb4ba162bb73c3cfc7", + "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60", + "reference": "dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60", "shasum": "" }, "type": "library", @@ -7862,28 +8617,27 @@ ], "support": { "issues": "https://github.com/swagger-api/swagger-ui/issues", - "source": "https://github.com/swagger-api/swagger-ui/tree/v5.30.3" + "source": "https://github.com/swagger-api/swagger-ui/tree/v5.32.6" }, - "time": "2025-11-25T12:39:47+00:00" + "time": "2026-05-12T09:35:37+00:00" }, { "name": "symfony/clock", - "version": "v7.4.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "9169f24776edde469914c1e7a1442a50f7a4e110" + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/9169f24776edde469914c1e7a1442a50f7a4e110", - "reference": "9169f24776edde469914c1e7a1442a50f7a4e110", + "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3", + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/clock": "^1.0", - "symfony/polyfill-php83": "^1.28" + "php": ">=8.4", + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -7922,7 +8676,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.4.0" + "source": "https://github.com/symfony/clock/tree/v8.0.8" }, "funding": [ { @@ -7942,20 +8696,20 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:39:26+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/console", - "version": "v7.4.0", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8" + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", + "url": "https://api.github.com/repos/symfony/console/zipball/85095d2573eaefaf35e40b9513a9bf09f72cd217", + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217", "shasum": "" }, "require": { @@ -8020,7 +8774,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.0" + "source": "https://github.com/symfony/console/tree/v7.4.13" }, "funding": [ { @@ -8040,24 +8794,24 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-05-24T08:56:14+00:00" }, { "name": "symfony/css-selector", - "version": "v7.4.0", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135" + "reference": "3665cfade90565430909b906394c73c8739e57d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/ab862f478513e7ca2fe9ec117a6f01a8da6e1135", - "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/3665cfade90565430909b906394c73c8739e57d0", + "reference": "3665cfade90565430909b906394c73c8739e57d0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -8089,7 +8843,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.4.0" + "source": "https://github.com/symfony/css-selector/tree/v8.0.9" }, "funding": [ { @@ -8109,20 +8863,20 @@ "type": "tidelift" } ], - "time": "2025-10-30T13:39:42+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { @@ -8135,7 +8889,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -8160,7 +8914,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.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -8171,25 +8925,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/error-handler", - "version": "v7.4.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2" + "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/48be2b0653594eea32dcef130cca1c811dcf25c2", - "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", + "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", "shasum": "" }, "require": { @@ -8238,7 +8996,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/v7.4.0" + "source": "https://github.com/symfony/error-handler/tree/v7.4.8" }, "funding": [ { @@ -8258,28 +9016,28 @@ "type": "tidelift" } ], - "time": "2025-11-05T14:29:59+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.4.0", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/security-http": "<7.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -8288,14 +9046,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0|^8.0" + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8323,7 +9081,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/v7.4.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9" }, "funding": [ { @@ -8343,20 +9101,20 @@ "type": "tidelift" } ], - "time": "2025-10-28T09:38:46+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { @@ -8370,7 +9128,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -8403,7 +9161,77 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-05T13:30:16+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v8.0.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/224db910898ce1317b892a9a1338f1f8f17eb7c7", + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^7.4|^8.0" + }, + "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/v8.0.11" }, "funding": [ { @@ -8414,25 +9242,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-05-11T16:39:47+00:00" }, { "name": "symfony/finder", - "version": "v7.4.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "340b9ed7320570f319028a2cbec46d40535e94bd" + "reference": "e0be088d22278583a82da281886e8c3592fbf149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/340b9ed7320570f319028a2cbec46d40535e94bd", - "reference": "340b9ed7320570f319028a2cbec46d40535e94bd", + "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", + "reference": "e0be088d22278583a82da281886e8c3592fbf149", "shasum": "" }, "require": { @@ -8467,7 +9299,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.0" + "source": "https://github.com/symfony/finder/tree/v7.4.8" }, "funding": [ { @@ -8487,20 +9319,20 @@ "type": "tidelift" } ], - "time": "2025-11-05T05:42:40+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.4.0", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "769c1720b68e964b13b58529c17d4a385c62167b" + "reference": "bc354f47c62301e990b7874fa662326368508e2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/769c1720b68e964b13b58529c17d4a385c62167b", - "reference": "769c1720b68e964b13b58529c17d4a385c62167b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/bc354f47c62301e990b7874fa662326368508e2c", + "reference": "bc354f47c62301e990b7874fa662326368508e2c", "shasum": "" }, "require": { @@ -8549,7 +9381,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.13" }, "funding": [ { @@ -8569,20 +9401,20 @@ "type": "tidelift" } ], - "time": "2025-11-13T08:49:24+00:00" + "time": "2026-05-24T11:20:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.0", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "7348193cd384495a755554382e4526f27c456085" + "reference": "9df847980c436451f4f51d1284491bb4356dd989" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7348193cd384495a755554382e4526f27c456085", - "reference": "7348193cd384495a755554382e4526f27c456085", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9df847980c436451f4f51d1284491bb4356dd989", + "reference": "9df847980c436451f4f51d1284491bb4356dd989", "shasum": "" }, "require": { @@ -8624,7 +9456,7 @@ "symfony/config": "^6.4|^7.0|^8.0", "symfony/console": "^6.4|^7.0|^8.0", "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", "symfony/dom-crawler": "^6.4|^7.0|^8.0", "symfony/expression-language": "^6.4|^7.0|^8.0", "symfony/finder": "^6.4|^7.0|^8.0", @@ -8668,7 +9500,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/v7.4.0" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.13" }, "funding": [ { @@ -8688,20 +9520,20 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:38:24+00:00" + "time": "2026-05-27T08:31:43+00:00" }, { "name": "symfony/mailer", - "version": "v7.4.0", + "version": "v7.4.12", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd" + "reference": "5cefb712a25f320579615ba9e1942abaeade7dff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", - "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", + "url": "https://api.github.com/repos/symfony/mailer/zipball/5cefb712a25f320579615ba9e1942abaeade7dff", + "reference": "5cefb712a25f320579615ba9e1942abaeade7dff", "shasum": "" }, "require": { @@ -8752,7 +9584,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.4.0" + "source": "https://github.com/symfony/mailer/tree/v7.4.12" }, "funding": [ { @@ -8772,20 +9604,20 @@ "type": "tidelift" } ], - "time": "2025-11-21T15:26:00+00:00" + "time": "2026-05-20T07:20:23+00:00" }, { "name": "symfony/mime", - "version": "v7.4.0", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a" + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/bdb02729471be5d047a3ac4a69068748f1a6be7a", - "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a", + "url": "https://api.github.com/repos/symfony/mime/zipball/a845722765c4f6b2ce88beaf4f4479975b186770", + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770", "shasum": "" }, "require": { @@ -8796,15 +9628,15 @@ }, "conflict": { "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", "symfony/mailer": "<6.4", "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", "symfony/dependency-injection": "^6.4|^7.0|^8.0", "symfony/process": "^6.4|^7.0|^8.0", "symfony/property-access": "^6.4|^7.0|^8.0", @@ -8841,7 +9673,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.4.0" + "source": "https://github.com/symfony/mime/tree/v7.4.13" }, "funding": [ { @@ -8861,20 +9693,20 @@ "type": "tidelift" } ], - "time": "2025-11-16T10:14:42+00:00" + "time": "2026-05-23T16:22:37+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -8924,7 +9756,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -8944,20 +9776,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { @@ -9006,7 +9838,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -9026,20 +9858,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + "reference": "dc21118016c039a66235cf93d96b435ffb282412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", "shasum": "" }, "require": { @@ -9093,7 +9925,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" }, "funding": [ { @@ -9113,20 +9945,20 @@ "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2026-05-25T15:22:23+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -9178,7 +10010,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -9198,20 +10030,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92", + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92", "shasum": "" }, "require": { @@ -9263,7 +10095,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1" }, "funding": [ { @@ -9283,20 +10115,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -9347,7 +10179,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -9367,20 +10199,20 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", "shasum": "" }, "require": { @@ -9427,7 +10259,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.38.1" }, "funding": [ { @@ -9447,20 +10279,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T12:45:58+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + "reference": "8339098cae28673c15cce00d80734af0453054e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/8339098cae28673c15cce00d80734af0453054e2", + "reference": "8339098cae28673c15cce00d80734af0453054e2", "shasum": "" }, "require": { @@ -9507,7 +10339,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.1" }, "funding": [ { @@ -9527,25 +10359,191 @@ "type": "tidelift" } ], - "time": "2025-07-08T02:45:35+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T12:51:13+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T02:25:22+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", "shasum": "" }, "require": { "php": ">=7.2" }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, "type": "library", "extra": { "thanks": { @@ -9558,11 +10556,8 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" - }, - "classmap": [ - "Resources/stubs" - ] + "Symfony\\Polyfill\\Uuid\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -9570,24 +10565,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "description": "Symfony polyfill for uuid functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", - "shim" + "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" }, "funding": [ { @@ -9607,41 +10602,32 @@ "type": "tidelift" } ], - "time": "2025-06-24T13:30:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "symfony/polyfill-php85", - "version": "v1.33.0", + "name": "symfony/process", + "version": "v7.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + "url": "https://github.com/symfony/process.git", + "reference": "f5804be144caceb570f6747519999636b664f24c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c", + "reference": "f5804be144caceb570f6747519999636b664f24c", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.2" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php85\\": "" + "Symfony\\Component\\Process\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -9650,24 +10636,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": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + "source": "https://github.com/symfony/process/tree/v7.4.13" }, "funding": [ { @@ -9687,45 +10667,38 @@ "type": "tidelift" } ], - "time": "2025-06-23T16:12:55+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { - "name": "symfony/polyfill-uuid", - "version": "v1.33.0", + "name": "symfony/property-access", + "version": "v8.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + "url": "https://github.com/symfony/property-access.git", + "reference": "704c7808116fcdd67327db7b17de56b8ef6169e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "url": "https://api.github.com/repos/symfony/property-access/zipball/704c7808116fcdd67327db7b17de56b8ef6169e4", + "reference": "704c7808116fcdd67327db7b17de56b8ef6169e4", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.4", + "symfony/property-info": "^7.4.4|^8.0.4" }, - "provide": { - "ext-uuid": "*" - }, - "suggest": { - "ext-uuid": "For best performance" + "require-dev": { + "symfony/cache": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Uuid\\": "" - } + "Symfony\\Component\\PropertyAccess\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -9733,24 +10706,29 @@ ], "authors": [ { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for uuid functions", + "description": "Provides functions to read and write from/to an object or array using a simple string notation", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "uuid" + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property-path", + "reflection" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + "source": "https://github.com/symfony/property-access/tree/v8.0.8" }, "funding": [ { @@ -9770,29 +10748,42 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { - "name": "symfony/process", - "version": "v7.4.0", + "name": "symfony/property-info", + "version": "v8.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8" + "url": "https://github.com/symfony/property-info.git", + "reference": "c21711980653360d6ef5c26d0f9ca6f58a1135c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", - "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", + "url": "https://api.github.com/repos/symfony/property-info/zipball/c21711980653360d6ef5c26d0f9ca6f58a1135c6", + "reference": "c21711980653360d6ef5c26d0f9ca6f58a1135c6", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4", + "symfony/string": "^7.4|^8.0", + "symfony/type-info": "^7.4.7|^8.0.7" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "symfony/cache": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Component\\PropertyInfo\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -9804,18 +10795,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Extracts information about PHP class' properties using metadata of popular sources", "homepage": "https://symfony.com", + "keywords": [ + "doctrine", + "phpdoc", + "property", + "symfony", + "type", + "validator" + ], "support": { - "source": "https://github.com/symfony/process/tree/v7.4.0" + "source": "https://github.com/symfony/property-info/tree/v8.0.8" }, "funding": [ { @@ -9835,41 +10834,40 @@ "type": "tidelift" } ], - "time": "2025-10-16T11:21:06+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v7.4.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "0101ff8bd0506703b045b1670960302d302a726c" + "reference": "94facc221260c1d5f20e31ee43cd6c6a824b4a19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/0101ff8bd0506703b045b1670960302d302a726c", - "reference": "0101ff8bd0506703b045b1670960302d302a726c", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/94facc221260c1d5f20e31ee43cd6c6a824b4a19", + "reference": "94facc221260c1d5f20e31ee43cd6c6a824b4a19", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/http-message": "^1.0|^2.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0" + "symfony/http-foundation": "^7.4|^8.0" }, "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-kernel": "<6.4" + "php-http/discovery": "<1.15" }, "require-dev": { "nyholm/psr7": "^1.1", "php-http/discovery": "^1.15", "psr/log": "^1.1.4|^2|^3", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", - "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", - "symfony/runtime": "^6.4.13|^7.1.6|^8.0" + "symfony/browser-kit": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -9903,7 +10901,7 @@ "psr-7" ], "support": { - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.4.0" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v8.0.8" }, "funding": [ { @@ -9923,20 +10921,20 @@ "type": "tidelift" } ], - "time": "2025-11-13T08:38:49+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/routing", - "version": "v7.4.0", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "4720254cb2644a0b876233d258a32bf017330db7" + "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/4720254cb2644a0b876233d258a32bf017330db7", - "reference": "4720254cb2644a0b876233d258a32bf017330db7", + "url": "https://api.github.com/repos/symfony/routing/zipball/3a162171bb008e5e0f15dce6581373a4c0e8390d", + "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d", "shasum": "" }, "require": { @@ -9988,7 +10986,105 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.4.0" + "source": "https://github.com/symfony/routing/tree/v7.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-24T11:20:33+00:00" + }, + { + "name": "symfony/serializer", + "version": "v8.0.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "72ed7e1475790714f07c3a59bd01fd32cd022fdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/72ed7e1475790714f07c3a59bd01fd32cd022fdf", + "reference": "72ed7e1475790714f07c3a59bd01fd32cd022fdf", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/property-access": "<7.4.2|>=8.0,<8.0.2", + "symfony/property-info": "<7.4", + "symfony/type-info": "<7.4" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "seld/jsonlint": "^1.10", + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/property-access": "^7.4.2|^8.0.2", + "symfony/property-info": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/type-info": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "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": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/serializer/tree/v8.0.10" }, "funding": [ { @@ -10008,20 +11104,20 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-05-04T13:41:39+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -10039,7 +11135,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -10075,7 +11171,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -10095,39 +11191,38 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v7.4.0", + "version": "v8.0.13", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" + "reference": "f2e3e4d33579350d1b12001ef2872f86b27ed3dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", - "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", + "url": "https://api.github.com/repos/symfony/string/zipball/f2e3e4d33579350d1b12001ef2872f86b27ed3dc", + "reference": "f2e3e4d33579350d1b12001ef2872f86b27ed3dc", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.33", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -10166,7 +11261,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.0" + "source": "https://github.com/symfony/string/tree/v8.0.13" }, "funding": [ { @@ -10186,38 +11281,31 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-05-23T18:05:53+00:00" }, { "name": "symfony/translation", - "version": "v7.4.0", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68" + "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", - "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", + "url": "https://api.github.com/repos/symfony/translation/zipball/f63e9342e12646a57c91ef8a366a4f9d8e557b67", + "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5.3|^3.3" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" }, "conflict": { "nikic/php-parser": "<5.0", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<6.4", - "symfony/yaml": "<6.4" + "symfony/service-contracts": "<2.5" }, "provide": { "symfony/translation-implementation": "2.3|3.0" @@ -10225,28 +11313,102 @@ "require-dev": { "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/routing": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v8.0.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-06T11:30:54+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } }, - "type": "library", "autoload": { - "files": [ - "Resources/functions.php" - ], "psr-4": { - "Symfony\\Component\\Translation\\": "" + "Symfony\\Contracts\\Translation\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -10255,18 +11417,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides tools to internationalize your application", + "description": "Generic abstractions related to translation", "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "source": "https://github.com/symfony/translation/tree/v7.4.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0" }, "funding": [ { @@ -10286,41 +11456,39 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { - "name": "symfony/translation-contracts", - "version": "v3.6.1", + "name": "symfony/type-info", + "version": "v8.0.9", "source": { "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + "url": "https://github.com/symfony/type-info.git", + "reference": "08723aceb8c3271e8cb3db8b2565728b0c88e866" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977", + "url": "https://api.github.com/repos/symfony/type-info/zipball/08723aceb8c3271e8cb3db8b2565728b0c88e866", + "reference": "08723aceb8c3271e8cb3db8b2565728b0c88e866", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.4", + "psr/container": "^1.1|^2.0" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } + "conflict": { + "phpstan/phpdoc-parser": "<1.30" + }, + "require-dev": { + "phpstan/phpdoc-parser": "^1.30|^2.0" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Contracts\\Translation\\": "" + "Symfony\\Component\\TypeInfo\\": "" }, "exclude-from-classmap": [ - "/Test/" + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -10329,26 +11497,28 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Mathias Arlaud", + "email": "mathias.arlaud@gmail.com" + }, + { + "name": "Baptiste LEDUC", + "email": "baptiste.leduc@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to translation", + "description": "Extracts PHP types information.", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "PHPStan", + "phpdoc", + "symfony", + "type" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/type-info/tree/v8.0.9" }, "funding": [ { @@ -10368,20 +11538,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/uid", - "version": "v7.4.0", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "2498e9f81b7baa206f44de583f2f48350b90142c" + "reference": "2676b524340abcfe4d6151ec698463cebafee439" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/2498e9f81b7baa206f44de583f2f48350b90142c", - "reference": "2498e9f81b7baa206f44de583f2f48350b90142c", + "url": "https://api.github.com/repos/symfony/uid/zipball/2676b524340abcfe4d6151ec698463cebafee439", + "reference": "2676b524340abcfe4d6151ec698463cebafee439", "shasum": "" }, "require": { @@ -10426,7 +11596,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.4.0" + "source": "https://github.com/symfony/uid/tree/v7.4.9" }, "funding": [ { @@ -10446,20 +11616,20 @@ "type": "tidelift" } ], - "time": "2025-09-25T11:02:55+00:00" + "time": "2026-04-30T15:19:22+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.4.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece" + "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41fd6c4ae28c38b294b42af6db61446594a0dece", - "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9510c3966f749a1d1ff0059e1eabef6cc621e7fd", + "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd", "shasum": "" }, "require": { @@ -10513,7 +11683,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.8" }, "funding": [ { @@ -10533,20 +11703,20 @@ "type": "tidelift" } ], - "time": "2025-10-27T20:36:44+00:00" + "time": "2026-03-30T13:44:50+00:00" }, { "name": "symfony/yaml", - "version": "v7.4.0", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810" + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", "shasum": "" }, "require": { @@ -10589,7 +11759,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.0" + "source": "https://github.com/symfony/yaml/tree/v7.4.13" }, "funding": [ { @@ -10609,7 +11779,7 @@ "type": "tidelift" } ], - "time": "2025-11-16T10:14:42+00:00" + "time": "2026-05-25T06:06:12+00:00" }, { "name": "theseer/tokenizer", @@ -10663,16 +11833,16 @@ }, { "name": "tightenco/ziggy", - "version": "v2.6.0", + "version": "v2.6.2", "source": { "type": "git", "url": "https://github.com/tighten/ziggy.git", - "reference": "cccc6035c109daab03a33926b3a8499bedbed01f" + "reference": "8a0b645921623f77dceaf543d61ecd51a391d96e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tighten/ziggy/zipball/cccc6035c109daab03a33926b3a8499bedbed01f", - "reference": "cccc6035c109daab03a33926b3a8499bedbed01f", + "url": "https://api.github.com/repos/tighten/ziggy/zipball/8a0b645921623f77dceaf543d61ecd51a391d96e", + "reference": "8a0b645921623f77dceaf543d61ecd51a391d96e", "shasum": "" }, "require": { @@ -10682,9 +11852,9 @@ }, "require-dev": { "laravel/folio": "^1.1", - "orchestra/testbench": "^7.0 || ^8.0 || ^9.0 || ^10.0", - "pestphp/pest": "^2.26|^3.0", - "pestphp/pest-plugin-laravel": "^2.4|^3.0" + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0", + "pestphp/pest": "^2.0 || ^3.0 || ^4.0", + "pestphp/pest-plugin-laravel": "^2.0 || ^3.0 || ^4.0" }, "type": "library", "extra": { @@ -10727,29 +11897,29 @@ ], "support": { "issues": "https://github.com/tighten/ziggy/issues", - "source": "https://github.com/tighten/ziggy/tree/v2.6.0" + "source": "https://github.com/tighten/ziggy/tree/v2.6.2" }, - "time": "2025-09-15T00:00:26+00:00" + "time": "2026-03-05T14:41:19+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^7.4 || ^8.0", - "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^2.0", @@ -10782,32 +11952,32 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" }, - "time": "2024-12-21T16:25:41+00:00" + "time": "2025-12-02T11:56:42+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.6.2", + "version": "v5.6.3", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + "reference": "955e7815d677a3eaa7075231212f2110983adecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", - "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.3", + "graham-campbell/result-type": "^1.1.4", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3", - "symfony/polyfill-ctype": "^1.24", - "symfony/polyfill-mbstring": "^1.24", - "symfony/polyfill-php80": "^1.24" + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", @@ -10856,7 +12026,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" }, "funding": [ { @@ -10868,27 +12038,27 @@ "type": "tidelift" } ], - "time": "2025-04-30T23:37:27+00:00" + "time": "2025-12-27T19:49:13+00:00" }, { "name": "voku/portable-ascii", - "version": "2.0.3", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + "reference": "8e1051fe39379367aecf014f41744ce7539a856f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" }, "suggest": { "ext-intl": "Use Intl for transliterator_transliterate() support" @@ -10918,7 +12088,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + "source": "https://github.com/voku/portable-ascii/tree/2.1.1" }, "funding": [ { @@ -10942,40 +12112,336 @@ "type": "tidelift" } ], - "time": "2024-11-21T01:49:47+00:00" + "time": "2026-04-26T05:33:54+00:00" + }, + { + "name": "web-auth/cose-lib", + "version": "4.5.2", + "source": { + "type": "git", + "url": "https://github.com/web-auth/cose-lib.git", + "reference": "5b38660f90070a8e45f3dbc9528ade3b608dd77d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/5b38660f90070a8e45f3dbc9528ade3b608dd77d", + "reference": "5b38660f90070a8e45f3dbc9528ade3b608dd77d", + "shasum": "" + }, + "require": { + "brick/math": "^0.9|^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17", + "ext-json": "*", + "ext-openssl": "*", + "php": ">=8.1", + "spomky-labs/pki-framework": "^1.0" + }, + "require-dev": { + "spomky-labs/cbor-php": "^3.2.2" + }, + "suggest": { + "ext-bcmath": "For better performance, please install either GMP (recommended) or BCMath extension", + "ext-gmp": "For better performance, please install either GMP (recommended) or BCMath extension", + "spomky-labs/cbor-php": "For COSE Signature support" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cose\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-auth/cose/contributors" + } + ], + "description": "CBOR Object Signing and Encryption (COSE) For PHP", + "homepage": "https://github.com/web-auth", + "keywords": [ + "COSE", + "RFC8152" + ], + "support": { + "issues": "https://github.com/web-auth/cose-lib/issues", + "source": "https://github.com/web-auth/cose-lib/tree/4.5.2" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2026-05-03T09:49:50+00:00" + }, + { + "name": "web-auth/webauthn-lib", + "version": "5.3.4", + "source": { + "type": "git", + "url": "https://github.com/web-auth/webauthn-lib.git", + "reference": "dbb2d7a03db5893da2ef1f2898063ab8f7792838" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/dbb2d7a03db5893da2ef1f2898063ab8f7792838", + "reference": "dbb2d7a03db5893da2ef1f2898063ab8f7792838", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "paragonie/constant_time_encoding": "^2.6|^3.0", + "php": ">=8.2", + "phpdocumentor/reflection-docblock": "^5.3|^6.0", + "psr/clock": "^1.0", + "psr/event-dispatcher": "^1.0", + "psr/log": "^1.0|^2.0|^3.0", + "spomky-labs/cbor-php": "^3.0", + "spomky-labs/pki-framework": "^1.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/deprecation-contracts": "^3.2", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "web-auth/cose-lib": "^4.2.3" + }, + "suggest": { + "psr/log-implementation": "Recommended to receive logs from the library", + "symfony/event-dispatcher": "Recommended to use dispatched events", + "web-token/jwt-library": "Mandatory for fetching Metadata Statement from distant sources" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/web-auth/webauthn-framework", + "name": "web-auth/webauthn-framework" + } + }, + "autoload": { + "psr-4": { + "Webauthn\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-auth/webauthn-library/contributors" + } + ], + "description": "FIDO2/Webauthn Support For PHP", + "homepage": "https://github.com/web-auth", + "keywords": [ + "FIDO2", + "fido", + "webauthn" + ], + "support": { + "source": "https://github.com/web-auth/webauthn-lib/tree/5.3.4" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2026-05-18T11:59:46+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9007ea6f45ecf352a9422b36644e4bfc039b9155", + "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.0" + }, + "time": "2026-05-20T13:07:01+00:00" + }, + { + "name": "whikloj/bagittools", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/whikloj/BagItTools.git", + "reference": "30697b4692cc874bd115b1b7d031001e7fe77f30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/whikloj/BagItTools/zipball/30697b4692cc874bd115b1b7d031001e7fe77f30", + "reference": "30697b4692cc874bd115b1b7d031001e7fe77f30", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-intl": "*", + "ext-mbstring": "*", + "ext-zip": "*", + "pear/archive_tar": "^1.4.14", + "php": ">=8.2", + "symfony/console": ">7" + }, + "require-dev": { + "donatj/mock-webserver": "^2.6", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "symfony": { + "allow-contrib": false + } + }, + "autoload": { + "psr-4": { + "whikloj\\BagItTools\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jared Whiklo", + "email": "jwhiklo@gmail.com", + "role": "Developer" + } + ], + "description": "A PHP library to manipulate and verify BagIt bags.", + "homepage": "https://github.com/whikloj/bagittools", + "keywords": [ + "bagit", + "bags", + "data", + "integrity", + "transmission" + ], + "support": { + "issues": "https://github.com/whikloj/BagItTools/issues", + "source": "https://github.com/whikloj/BagItTools/tree/6.0.0" + }, + "time": "2026-01-07T22:17:43+00:00" }, { "name": "zircote/swagger-php", - "version": "4.11.1", + "version": "5.8.3", "source": { "type": "git", "url": "https://github.com/zircote/swagger-php.git", - "reference": "7df10e8ec47db07c031db317a25bef962b4e5de1" + "reference": "098223019f764a16715f64089a58606096719c98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/7df10e8ec47db07c031db317a25bef962b4e5de1", - "reference": "7df10e8ec47db07c031db317a25bef962b4e5de1", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/098223019f764a16715f64089a58606096719c98", + "reference": "098223019f764a16715f64089a58606096719c98", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=7.2", + "nikic/php-parser": "^4.19 || ^5.0", + "php": ">=7.4", + "phpstan/phpdoc-parser": "^2.0", "psr/log": "^1.1 || ^2.0 || ^3.0", "symfony/deprecation-contracts": "^2 || ^3", - "symfony/finder": ">=2.2", - "symfony/yaml": ">=3.3" + "symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "conflict": { + "symfony/process": ">=6, <6.4.14" }, "require-dev": { "composer/package-versions-deprecated": "^1.11", - "doctrine/annotations": "^1.7 || ^2.0", - "friendsofphp/php-cs-fixer": "^2.17 || 3.62.0", - "phpstan/phpstan": "^1.6", - "phpunit/phpunit": ">=8", - "vimeo/psalm": "^4.23" + "doctrine/annotations": "^2.0", + "friendsofphp/php-cs-fixer": "^3.62.0", + "phpstan/phpstan": "^1.6 || ^2.0", + "phpunit/phpunit": "^9.0", + "rector/rector": "^1.0 || ^2.3.1", + "vimeo/psalm": "^4.30 || ^5.0" }, "suggest": { - "doctrine/annotations": "^1.7 || ^2.0" + "doctrine/annotations": "^2.0", + "radebatz/type-info-extras": "^1.0.2" }, "bin": [ "bin/openapi" @@ -10983,7 +12449,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -11011,8 +12477,8 @@ "homepage": "https://radebatz.net" } ], - "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", - "homepage": "https://github.com/zircote/swagger-php/", + "description": "Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations", + "homepage": "https://github.com/zircote/swagger-php", "keywords": [ "api", "json", @@ -11021,24 +12487,30 @@ ], "support": { "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/4.11.1" + "source": "https://github.com/zircote/swagger-php/tree/5.8.3" }, - "time": "2024-10-15T19:20:02+00:00" + "funding": [ + { + "url": "https://github.com/zircote", + "type": "github" + } + ], + "time": "2026-03-02T00:47:18+00:00" } ], "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.8.4", + "version": "v7.8.5", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4" + "reference": "9b324c8fc319cf9728b581c7a90e1c8f6361c5e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/130a9bf0e269ee5f5b320108f794ad03e275cad4", - "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/9b324c8fc319cf9728b581c7a90e1c8f6361c5e5", + "reference": "9b324c8fc319cf9728b581c7a90e1c8f6361c5e5", "shasum": "" }, "require": { @@ -11046,27 +12518,27 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.2.0", + "fidry/cpu-core-counter": "^1.3.0", "jean85/pretty-package-versions": "^2.1.1", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^11.0.10", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^11.0.12", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-timer": "^7.0.1", - "phpunit/phpunit": "^11.5.24", + "phpunit/phpunit": "^11.5.46", "sebastian/environment": "^7.2.1", - "symfony/console": "^6.4.22 || ^7.3.0", - "symfony/process": "^6.4.20 || ^7.3.0" + "symfony/console": "^6.4.22 || ^7.3.4 || ^8.0.3", + "symfony/process": "^6.4.20 || ^7.3.4 || ^8.0.3" }, "require-dev": { "doctrine/coding-standard": "^12.0.0", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan": "^2.1.33", "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.6", - "phpstan/phpstan-strict-rules": "^2.0.4", - "squizlabs/php_codesniffer": "^3.13.2", - "symfony/filesystem": "^6.4.13 || ^7.3.0" + "phpstan/phpstan-phpunit": "^2.0.11", + "phpstan/phpstan-strict-rules": "^2.0.7", + "squizlabs/php_codesniffer": "^3.13.5", + "symfony/filesystem": "^6.4.13 || ^7.3.2 || ^8.0.1" }, "bin": [ "bin/paratest", @@ -11106,7 +12578,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.8.4" + "source": "https://github.com/paratestphp/paratest/tree/v7.8.5" }, "funding": [ { @@ -11118,7 +12590,7 @@ "type": "paypal" } ], - "time": "2025-06-23T06:07:21+00:00" + "time": "2026-01-08T08:02:38+00:00" }, { "name": "fakerphp/faker", @@ -11428,34 +12900,34 @@ }, { "name": "laravel/boost", - "version": "v1.8.3", + "version": "v1.8.13", "source": { "type": "git", "url": "https://github.com/laravel/boost.git", - "reference": "26572e858e67334952779c0110ca4c378a44d28d" + "reference": "cdcb12114315491f72a2cecb5130d8b9dffa0103" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/boost/zipball/26572e858e67334952779c0110ca4c378a44d28d", - "reference": "26572e858e67334952779c0110ca4c378a44d28d", + "url": "https://api.github.com/repos/laravel/boost/zipball/cdcb12114315491f72a2cecb5130d8b9dffa0103", + "reference": "cdcb12114315491f72a2cecb5130d8b9dffa0103", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^7.9", - "illuminate/console": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/contracts": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/routing": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/support": "^10.49.0|^11.45.3|^12.28.1", - "laravel/mcp": "^0.3.4", + "illuminate/console": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/contracts": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/routing": "^10.49.0|^11.45.3|^12.41.1", + "illuminate/support": "^10.49.0|^11.45.3|^12.41.1", + "laravel/mcp": "^0.5.1", "laravel/prompts": "0.1.25|^0.3.6", "laravel/roster": "^0.2.9", "php": "^8.1" }, "require-dev": { - "laravel/pint": "1.20", + "laravel/pint": "^1.20.0", "mockery/mockery": "^1.6.12", "orchestra/testbench": "^8.36.0|^9.15.0|^10.6", - "pestphp/pest": "^2.36.0|^3.8.4", + "pestphp/pest": "^2.36.0|^3.8.4|^4.1.5", "phpstan/phpstan": "^2.1.27", "rector/rector": "^2.1" }, @@ -11490,39 +12962,39 @@ "issues": "https://github.com/laravel/boost/issues", "source": "https://github.com/laravel/boost" }, - "time": "2025-11-26T14:12:52+00:00" + "time": "2026-03-27T17:24:01+00:00" }, { "name": "laravel/mcp", - "version": "v0.3.4", + "version": "v0.5.9", "source": { "type": "git", "url": "https://github.com/laravel/mcp.git", - "reference": "0b86fb613a0df971cec89271c674a677c2cb4f77" + "reference": "39e8da60eb7bce4737c5d868d35a3fe78938c129" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/mcp/zipball/0b86fb613a0df971cec89271c674a677c2cb4f77", - "reference": "0b86fb613a0df971cec89271c674a677c2cb4f77", + "url": "https://api.github.com/repos/laravel/mcp/zipball/39e8da60eb7bce4737c5d868d35a3fe78938c129", + "reference": "39e8da60eb7bce4737c5d868d35a3fe78938c129", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "illuminate/console": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/container": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/contracts": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/http": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/json-schema": "^12.28.1", - "illuminate/routing": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/support": "^10.49.0|^11.45.3|^12.28.1", - "illuminate/validation": "^10.49.0|^11.45.3|^12.28.1", - "php": "^8.1" + "illuminate/console": "^11.45.3|^12.41.1|^13.0", + "illuminate/container": "^11.45.3|^12.41.1|^13.0", + "illuminate/contracts": "^11.45.3|^12.41.1|^13.0", + "illuminate/http": "^11.45.3|^12.41.1|^13.0", + "illuminate/json-schema": "^12.41.1|^13.0", + "illuminate/routing": "^11.45.3|^12.41.1|^13.0", + "illuminate/support": "^11.45.3|^12.41.1|^13.0", + "illuminate/validation": "^11.45.3|^12.41.1|^13.0", + "php": "^8.2" }, "require-dev": { - "laravel/pint": "1.20.0", - "orchestra/testbench": "^8.36.0|^9.15.0|^10.6.0", - "pestphp/pest": "^2.36.0|^3.8.4|^4.1.0", + "laravel/pint": "^1.20", + "orchestra/testbench": "^9.15|^10.8|^11.0", + "pestphp/pest": "^3.8.5|^4.3.2", "phpstan/phpstan": "^2.1.27", "rector/rector": "^2.2.4" }, @@ -11563,41 +13035,42 @@ "issues": "https://github.com/laravel/mcp/issues", "source": "https://github.com/laravel/mcp" }, - "time": "2025-11-18T14:41:05+00:00" + "time": "2026-02-17T19:05:53+00:00" }, { "name": "laravel/pail", - "version": "v1.2.4", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/laravel/pail.git", - "reference": "49f92285ff5d6fc09816e976a004f8dec6a0ea30" + "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pail/zipball/49f92285ff5d6fc09816e976a004f8dec6a0ea30", - "reference": "49f92285ff5d6fc09816e976a004f8dec6a0ea30", + "url": "https://api.github.com/repos/laravel/pail/zipball/2f7d27dada8effc48b8c424445a69cca7007daaa", + "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa", "shasum": "" }, "require": { "ext-mbstring": "*", - "illuminate/console": "^10.24|^11.0|^12.0", - "illuminate/contracts": "^10.24|^11.0|^12.0", - "illuminate/log": "^10.24|^11.0|^12.0", - "illuminate/process": "^10.24|^11.0|^12.0", - "illuminate/support": "^10.24|^11.0|^12.0", + "illuminate/console": "^10.24|^11.0|^12.0|^13.0", + "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", + "illuminate/log": "^10.24|^11.0|^12.0|^13.0", + "illuminate/process": "^10.24|^11.0|^12.0|^13.0", + "illuminate/support": "^10.24|^11.0|^12.0|^13.0", "nunomaduro/termwind": "^1.15|^2.0", "php": "^8.2", - "symfony/console": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0" }, "require-dev": { - "laravel/framework": "^10.24|^11.0|^12.0", + "laravel/framework": "^10.24|^11.0|^12.0|^13.0", "laravel/pint": "^1.13", - "orchestra/testbench-core": "^8.13|^9.17|^10.8", + "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", "pestphp/pest": "^2.20|^3.0|^4.0", "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", "phpstan/phpstan": "^1.12.27", - "symfony/var-dumper": "^6.3|^7.0" + "symfony/var-dumper": "^6.3|^7.0|^8.0", + "symfony/yaml": "^6.3|^7.0|^8.0" }, "type": "library", "extra": { @@ -11642,20 +13115,20 @@ "issues": "https://github.com/laravel/pail/issues", "source": "https://github.com/laravel/pail" }, - "time": "2025-11-20T16:29:35+00:00" + "time": "2026-05-20T22:24:57+00:00" }, { "name": "laravel/pint", - "version": "v1.26.0", + "version": "v1.29.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f" + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/69dcca060ecb15e4b564af63d1f642c81a241d6f", - "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f", + "url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80", + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80", "shasum": "" }, "require": { @@ -11666,13 +13139,14 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.90.0", - "illuminate/view": "^12.40.1", - "larastan/larastan": "^3.8.0", - "laravel-zero/framework": "^12.0.4", + "friendsofphp/php-cs-fixer": "^3.95.1", + "illuminate/view": "^12.56.0", + "larastan/larastan": "^3.9.6", + "laravel-zero/framework": "^12.1.0", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.3.3", - "pestphp/pest": "^3.8.4" + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6", + "shipfastlabs/agent-detector": "^1.1.3" }, "bin": [ "builds/pint" @@ -11709,7 +13183,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-11-25T21:15:52+00:00" + "time": "2026-04-20T15:26:14+00:00" }, { "name": "laravel/roster", @@ -11774,28 +13248,28 @@ }, { "name": "laravel/sail", - "version": "v1.49.0", + "version": "v1.61.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "070c7f34ca8dbece4350fbfe0bab580047dfacc7" + "reference": "68ef35015630fe510432e63e11e21749006df688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/070c7f34ca8dbece4350fbfe0bab580047dfacc7", - "reference": "070c7f34ca8dbece4350fbfe0bab580047dfacc7", + "url": "https://api.github.com/repos/laravel/sail/zipball/68ef35015630fe510432e63e11e21749006df688", + "reference": "68ef35015630fe510432e63e11e21749006df688", "shasum": "" }, "require": { - "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0", - "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0", - "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", - "symfony/console": "^6.0|^7.0", - "symfony/yaml": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/yaml": "^6.0|^7.0|^8.0" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", "phpstan/phpstan": "^2.0" }, "bin": [ @@ -11833,7 +13307,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-11-25T21:15:57+00:00" + "time": "2026-05-23T23:33:57+00:00" }, { "name": "mockery/mockery", @@ -11980,39 +13454,36 @@ }, { "name": "nunomaduro/collision", - "version": "v8.8.3", + "version": "v8.9.4", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4" + "reference": "716af8f95a470e9094cfca09ed897b023be191a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4", - "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5", "shasum": "" }, "require": { - "filp/whoops": "^2.18.1", - "nunomaduro/termwind": "^2.3.1", + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", "php": "^8.2.0", - "symfony/console": "^7.3.0" + "symfony/console": "^7.4.8 || ^8.0.8" }, "conflict": { - "laravel/framework": "<11.44.2 || >=13.0.0", - "phpunit/phpunit": "<11.5.15 || >=13.0.0" + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" }, "require-dev": { - "brianium/paratest": "^7.8.3", - "larastan/larastan": "^3.4.2", - "laravel/framework": "^11.44.2 || ^12.18", - "laravel/pint": "^1.22.1", - "laravel/sail": "^1.43.1", - "laravel/sanctum": "^4.1.1", - "laravel/tinker": "^2.10.1", - "orchestra/testbench-core": "^9.12.0 || ^10.4", - "pestphp/pest": "^3.8.2 || ^4.0.0", - "sebastian/environment": "^7.2.1 || ^8.0" + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.6", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", + "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" }, "type": "library", "extra": { @@ -12075,7 +13546,7 @@ "type": "patreon" } ], - "time": "2025-11-20T02:55:25+00:00" + "time": "2026-04-21T14:04:20+00:00" }, { "name": "phar-io/manifest", @@ -12321,16 +13792,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.45", + "version": "11.5.55", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "faf5fff4fb9beb290affa53f812b05380819c51a" + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/faf5fff4fb9beb290affa53f812b05380819c51a", - "reference": "faf5fff4fb9beb290affa53f812b05380819c51a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", "shasum": "" }, "require": { @@ -12344,19 +13815,20 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.11", - "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.1", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.2", + "sebastian/comparator": "^6.3.3", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.1", "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", + "sebastian/recursion-context": "^6.0.3", "sebastian/type": "^5.1.3", "sebastian/version": "^5.0.2", "staabm/side-effects-detector": "^1.0.5" @@ -12402,7 +13874,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.45" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" }, "funding": [ { @@ -12426,7 +13898,7 @@ "type": "tidelift" } ], - "time": "2025-12-01T07:38:43+00:00" + "time": "2026-02-18T12:37:06+00:00" }, { "name": "sebastian/cli-parser", @@ -12544,16 +14016,16 @@ }, { "name": "sebastian/comparator", - "version": "6.3.2", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", "shasum": "" }, "require": { @@ -12612,7 +14084,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" }, "funding": [ { @@ -12632,7 +14104,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T08:07:46+00:00" + "time": "2026-01-24T09:26:40+00:00" }, { "name": "sebastian/diff", @@ -13171,11 +14643,8 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.2" + "php": "^8.4" }, "platform-dev": {}, - "platform-overrides": { - "php": "8.3.28" - }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/app.php b/config/app.php new file mode 100644 index 000000000..6ddfbb0f3 --- /dev/null +++ b/config/app.php @@ -0,0 +1,79 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | BrowserSync client (local only, optional) + |-------------------------------------------------------------------------- + | + | When set (e.g. http://localhost:3000), app.blade.php loads the BrowserSync + | client for live reload. Leave unset if BrowserSync is not running on that + | port (avoids net::ERR_CONNECTION_REFUSED in the console). + | + */ + + 'browser_sync_client_url' => env('BROWSER_SYNC_CLIENT_URL'), + + /* + |-------------------------------------------------------------------------- + | Application Description + |-------------------------------------------------------------------------- + | + | A brief description of your application. + | + */ + + 'description' => env('APP_DESCRIPTION', 'NMRXIV is an open-access preprint repository for sharing and discovering nuclear magnetic resonance (NMR) spectroscopy data.'), + + /* + |-------------------------------------------------------------------------- + | Schema Version + |-------------------------------------------------------------------------- + | + | The schema version used by the application for data structures. + | + */ + + 'schema_version' => env('SCHEMA_VERSION', 'beta'), + +]; diff --git a/config/audit.php b/config/audit.php index 9c94d549e..46f6dd11c 100644 --- a/config/audit.php +++ b/config/audit.php @@ -1,5 +1,11 @@ env('AUDITING_ENABLED', true), @@ -13,7 +19,7 @@ | */ - 'implementation' => OwenIt\Auditing\Models\Audit::class, + 'implementation' => Audit::class, /* |-------------------------------------------------------------------------- @@ -30,7 +36,7 @@ 'web', 'api', ], - 'resolver' => OwenIt\Auditing\Resolvers\UserResolver::class, + 'resolver' => UserResolver::class, ], /* @@ -42,9 +48,9 @@ | */ 'resolvers' => [ - 'ip_address' => OwenIt\Auditing\Resolvers\IpAddressResolver::class, - 'user_agent' => OwenIt\Auditing\Resolvers\UserAgentResolver::class, - 'url' => OwenIt\Auditing\Resolvers\UrlResolver::class, + 'ip_address' => IpAddressResolver::class, + 'user_agent' => UserAgentResolver::class, + 'url' => UrlResolver::class, ], /* diff --git a/config/backup.php b/config/backup.php index 2bcc6148f..a8c617038 100644 --- a/config/backup.php +++ b/config/backup.php @@ -1,5 +1,16 @@ [ @@ -151,19 +162,19 @@ 'notifications' => [ 'notifications' => [ - \Spatie\Backup\Notifications\Notifications\BackupHasFailedNotification::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFoundNotification::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\CleanupHasFailedNotification::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\BackupWasSuccessfulNotification::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\HealthyBackupWasFoundNotification::class => ['mail'], - \Spatie\Backup\Notifications\Notifications\CleanupWasSuccessfulNotification::class => ['mail'], + BackupHasFailedNotification::class => ['mail'], + UnhealthyBackupWasFoundNotification::class => ['mail'], + CleanupHasFailedNotification::class => ['mail'], + BackupWasSuccessfulNotification::class => ['mail'], + HealthyBackupWasFoundNotification::class => ['mail'], + CleanupWasSuccessfulNotification::class => ['mail'], ], /* * Here you can specify the notifiable to which the notifications should be sent. The default * notifiable will use the variables specified in this config file. */ - 'notifiable' => \Spatie\Backup\Notifications\Notifiable::class, + 'notifiable' => Notifiable::class, 'mail' => [ 'to' => env('MAIL_FROM_ADDRESS', 'info.nmrxiv@uni-jena.de'), @@ -213,8 +224,8 @@ 'name' => env('APP_NAME', 'laravel-backup'), 'disks' => ['local'], 'health_checks' => [ - \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1, - \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000, + MaximumAgeInDays::class => 1, + MaximumStorageInMegabytes::class => 5000, ], ], @@ -240,7 +251,7 @@ * No matter how you configure it the default strategy will never * delete the newest backup. */ - 'strategy' => \Spatie\Backup\Tasks\Cleanup\Strategies\DefaultStrategy::class, + 'strategy' => DefaultStrategy::class, 'default_strategy' => [ diff --git a/config/csp.php b/config/csp.php new file mode 100644 index 000000000..624bc267d --- /dev/null +++ b/config/csp.php @@ -0,0 +1,90 @@ + [ + // Enforcement mode - now using secure CSP policy + NmrxivPolicy::class, + ], + + /** + * Register additional global CSP directives here. + * These can be configured via environment variables for runtime flexibility. + */ + 'directives' => [ + // Additional connect-src domains (configurable via env) + ...(env('CSP_ADDITIONAL_CONNECT_SRC') ? [ + [Directive::CONNECT, array_filter(explode(',', env('CSP_ADDITIONAL_CONNECT_SRC')))], + ] : []), + + // Additional img-src domains (configurable via env) + ...(env('CSP_ADDITIONAL_IMG_SRC') ? [ + [Directive::IMG, array_filter(explode(',', env('CSP_ADDITIONAL_IMG_SRC')))], + ] : []), + + // Additional script-src domains (configurable via env) + ...(env('CSP_ADDITIONAL_SCRIPT_SRC') ? [ + [Directive::SCRIPT, array_filter(explode(',', env('CSP_ADDITIONAL_SCRIPT_SRC')))], + ] : []), + + // Additional style-src domains (configurable via env) + ...(env('CSP_ADDITIONAL_STYLE_SRC') ? [ + [Directive::STYLE, array_filter(explode(',', env('CSP_ADDITIONAL_STYLE_SRC')))], + ] : []), + ], + + /* + * These presets which will be put in a report-only policy. This is great for testing out + * a new policy or changes to existing CSP policy without breaking anything. + */ + 'report_only_presets' => [ + // Moved to enforcement mode above + ], + + /** + * Register additional global report-only CSP directives here. + */ + 'report_only_directives' => [ + // [Directive::SCRIPT, [Keyword::UNSAFE_EVAL, Keyword::UNSAFE_INLINE]], + ], + + /* + * All violations against a policy will be reported to this url. + * Set to null to disable violation reporting. + */ + 'report_uri' => null, + + /* + * Headers will only be added if this setting is set to true. + */ + 'enabled' => env('CSP_ENABLED', true), + + /** + * Headers will be added when Vite is hot reloading. + */ + 'enabled_while_hot_reloading' => env('CSP_ENABLED_WHILE_HOT_RELOADING', true), + + /* + * The class responsible for generating the nonces used in inline tags and headers. + */ + 'nonce_generator' => RandomString::class, + + /* + * Set false to disable automatic nonce generation and handling. + * This is useful when you want to use 'unsafe-inline' for scripts/styles + * and cannot add inline nonces. + * Note that this will make your CSP policy less secure. + */ + 'nonce_enabled' => env('CSP_NONCE_ENABLED', true), +]; diff --git a/config/doi.php b/config/doi.php index 4855e6f31..c61257b14 100644 --- a/config/doi.php +++ b/config/doi.php @@ -14,6 +14,47 @@ 'default' => env('DOI_PROVIDER', 'datacite'), + /* + |-------------------------------------------------------------------------- + | DOI Host + |-------------------------------------------------------------------------- + | + | The DOI host URL used for resolving DOI identifiers. + | + */ + + 'host' => env('DOI_HOST', 'https://doi.org'), + + /* + |-------------------------------------------------------------------------- + | Publisher (DataCite 4.4) + |-------------------------------------------------------------------------- + | + | DataCite Schema 4.4 supports `publisher` as an object that can carry a + | persistent identifier (ROR) for the repository itself, making the host + | repository citable in the DataCite graph. When `publisher_ror` is null, + | the identifier subkeys are dropped at serialization time. + | + */ + + 'publisher_name' => env('DOI_PUBLISHER_NAME', 'nmrXiv'), + 'publisher_ror' => env('DOI_PUBLISHER_ROR'), + + /* + |-------------------------------------------------------------------------- + | Related software DOI + |-------------------------------------------------------------------------- + | + | When set, every dataset whose spectra were imported via this software + | gets an `IsCompiledBy` related-identifier entry pointing at the + | software's DOI. Defaults to NMRium's published DOI on Zenodo. + | + */ + + 'related_software' => [ + 'nmrium' => env('DOI_NMRIUM_DOI', '10.5281/zenodo.10209593'), + ], + /* |-------------------------------------------------------------------------- | DataCite Options diff --git a/config/external-links.php b/config/external-links.php new file mode 100644 index 000000000..46b7ce5fa --- /dev/null +++ b/config/external-links.php @@ -0,0 +1,39 @@ + env('MICHI_STANDARDS_URL'), + + /* + |-------------------------------------------------------------------------- + | External API Endpoints + |-------------------------------------------------------------------------- + | + | API endpoints for external services used by the application. + | + */ + + 'nmrium_url' => env('NMRIUM_URL', 'https://nmrium.nmrxiv.org'), + 'nmrkit_url' => env('NMRKIT_URL', 'https://nodejs.nmrxiv.org'), + 'europemc_ws_api' => env('EUROPEMC_WS_API', 'https://www.ebi.ac.uk/europepmc/webservices/rest/search'), + 'cm_api' => env('CM_API', 'https://api.cheminf.studio'), + 'crossref_api' => env('CROSSREF_API', 'https://api.crossref.org/works/'), + 'datacite_api' => env('DATACITE_API', 'https://api.datacite.org'), + +]; diff --git a/config/filesystems.php b/config/filesystems.php index 1e7f28e49..9ad0e21e8 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -35,6 +35,12 @@ 'serve' => true, 'throw' => false, 'report' => false, + // AWS S3 config keys for testing (when StorageSignedUrlService is used) + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'endpoint' => env('AWS_URL'), ], 'ceph' => [ diff --git a/config/l5-swagger.php b/config/l5-swagger.php index 31ed18a3f..779030533 100644 --- a/config/l5-swagger.php +++ b/config/l5-swagger.php @@ -1,5 +1,8 @@ 'default', 'documentations' => [ @@ -106,14 +109,14 @@ /** * analyser: defaults to \OpenApi\StaticAnalyser . * - * @see \OpenApi\scan + * @see scan */ 'analyser' => null, /** * analysis: defaults to a new \OpenApi\Analysis . * - * @see \OpenApi\scan + * @see scan */ 'analysis' => null, @@ -121,7 +124,7 @@ * Custom query path processors classes. * * @link https://github.com/zircote/swagger-php/tree/master/Examples/schema-query-parameter-processor - * @see \OpenApi\scan + * @see scan */ 'processors' => [ // new \App\SwaggerProcessors\SchemaQueryParameter(), @@ -130,7 +133,7 @@ /** * pattern: string $pattern File pattern(s) to scan (default: *.php) . * - * @see \OpenApi\scan + * @see scan */ 'pattern' => null, @@ -145,7 +148,7 @@ * Allows to generate specs either for OpenAPI 3.0.0 or OpenAPI 3.1.0. * By default the spec will be in version 3.0.0 */ - 'open_api_spec_version' => env('L5_SWAGGER_OPEN_API_SPEC_VERSION', \L5Swagger\Generator::OPEN_API_DEFAULT_SPEC_VERSION), + 'open_api_spec_version' => env('L5_SWAGGER_OPEN_API_SPEC_VERSION', Generator::OPEN_API_DEFAULT_SPEC_VERSION), ], /* diff --git a/config/larabug.php b/config/larabug.php deleted file mode 100644 index ddc0026bd..000000000 --- a/config/larabug.php +++ /dev/null @@ -1,146 +0,0 @@ - env('LB_KEY', ''), - - /* - |-------------------------------------------------------------------------- - | Project key - |-------------------------------------------------------------------------- - | - | This is your project key which you receive when creating a project - | Retrieve your key from https://www.larabug.com - | - */ - - 'project_key' => env('LB_PROJECT_KEY', ''), - - /* - |-------------------------------------------------------------------------- - | Environment setting - |-------------------------------------------------------------------------- - | - | This setting determines if the exception should be send over or not. - | - */ - - 'environments' => [ - 'production', - 'development', - ], - - /* - |-------------------------------------------------------------------------- - | Project version - |-------------------------------------------------------------------------- - | - | Set the project version, default: null. - | For git repository: shell_exec("git log -1 --pretty=format:'%h' --abbrev-commit") - | - */ - 'project_version' => null, - - /* - |-------------------------------------------------------------------------- - | Lines near exception - |-------------------------------------------------------------------------- - | - | How many lines to show near exception line. The more you specify the bigger - | the displayed code will be. Max value can be 50, will be defaulted to - | 12 if higher than 50 automatically. - | - */ - - 'lines_count' => 12, - - /* - |-------------------------------------------------------------------------- - | Prevent duplicates - |-------------------------------------------------------------------------- - | - | Set the sleep time between duplicate exceptions. This value is in seconds, default: 60 seconds (1 minute) - | - */ - - 'sleep' => 60, - - /* - |-------------------------------------------------------------------------- - | Skip exceptions - |-------------------------------------------------------------------------- - | - | List of exceptions to skip sending. - | - */ - - 'except' => [ - 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException', - ], - - /* - |-------------------------------------------------------------------------- - | Key filtering - |-------------------------------------------------------------------------- - | - | Filter out these variables before sending them to LaraBug - | - */ - - 'blacklist' => [ - '*authorization*', - '*password*', - '*token*', - '*auth*', - '*verification*', - '*credit_card*', - 'cardToken', // mollie card token - '*cvv*', - '*iban*', - '*name*', - '*email*', - ], - - /* - |-------------------------------------------------------------------------- - | Release git hash - |-------------------------------------------------------------------------- - | - | - */ - - // 'release' => trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')), - - /* - |-------------------------------------------------------------------------- - | Server setting - |-------------------------------------------------------------------------- - | - | This setting allows you to change the server. - | - */ - - 'server' => env('LB_SERVER', 'https://www.larabug.com/api/log'), - - /* - |-------------------------------------------------------------------------- - | Verify SSL setting - |-------------------------------------------------------------------------- - | - | Enables / disables the SSL verification when sending exceptions to LaraBug - | Never turn SSL verification off on production instances - | - */ - 'verify_ssl' => env('LB_VERIFY_SSL', true), - -]; diff --git a/config/logging.php b/config/logging.php deleted file mode 100644 index 6348a1239..000000000 --- a/config/logging.php +++ /dev/null @@ -1,11 +0,0 @@ - [ - 'larabug' => [ - 'driver' => 'larabug', - ], - ], - -]; diff --git a/config/mail.php b/config/mail.php index 1cb555c51..78b331772 100644 --- a/config/mail.php +++ b/config/mail.php @@ -22,6 +22,22 @@ ], ], + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + 'markdown' => [ 'theme' => 'default', diff --git a/config/markable.php b/config/markable.php index 4ff4333b3..c5722db6c 100644 --- a/config/markable.php +++ b/config/markable.php @@ -1,5 +1,7 @@ App\Models\User::class, + 'user_model' => User::class, /* |-------------------------------------------------------------------------- diff --git a/config/nmrxiv.php b/config/nmrxiv.php new file mode 100644 index 000000000..2d8d5ae36 --- /dev/null +++ b/config/nmrxiv.php @@ -0,0 +1,46 @@ + (int) env('COOL_OFF_PERIOD', 30), + + /* + |-------------------------------------------------------------------------- + | Spectra Parsing Configuration + |-------------------------------------------------------------------------- + | + | Configuration for the spectra parsing queue system including API endpoints, + | storage locations, retry logic, and timeout values. + | + */ + + 'spectra_parsing' => [ + // API Endpoints + 'nmrkit_api_url' => env('NMRKIT_API_URL', 'https://nmrkit.nmrxiv.org/latest/spectra/parse/url'), + 'bioschema_api_url' => env('BIOSCHEMA_API_URL', 'https://nmrxiv.org/api/v1/schemas/bioschemas'), + + // Storage Configuration + 'storage_disk' => env('SPECTRA_STORAGE_DISK', 'local'), + 'storage_path' => env('SPECTRA_STORAGE_PATH', 'spectra_parse'), + + // Job Configuration + 'job_tries' => (int) env('SPECTRA_JOB_TRIES', 3), + 'job_timeout' => (int) env('SPECTRA_JOB_TIMEOUT', 600), + + // Network Configuration + 'retry_count' => (int) env('SPECTRA_RETRY_COUNT', 3), + 'download_timeout' => (int) env('SPECTRA_DOWNLOAD_TIMEOUT', 300), + 'api_timeout' => (int) env('SPECTRA_API_TIMEOUT', 300), + ], + +]; diff --git a/config/orcid.php b/config/orcid.php new file mode 100644 index 000000000..add894032 --- /dev/null +++ b/config/orcid.php @@ -0,0 +1,29 @@ + env('ORCID_BASE_URL', 'https://pub.orcid.org/v3.0'), + + /* + |-------------------------------------------------------------------------- + | ORCID API Endpoints + |-------------------------------------------------------------------------- + | + | Various ORCID API endpoints for searching and retrieving person data. + | + */ + + 'search_api' => env('ORCID_ID_SEARCH_API', 'https://pub.orcid.org/v3.0/search'), + 'person_api' => env('ORCID_ID_PERSON_API', 'https://pub.orcid.org/v3.0'), + 'employment_api' => env('ORCID_ID_EMPLOYMENT_API', 'https://pub.orcid.org/v3.0'), + +]; diff --git a/config/permission.php b/config/permission.php index c306a8d50..106f3159f 100644 --- a/config/permission.php +++ b/config/permission.php @@ -1,5 +1,8 @@ [ @@ -13,7 +16,7 @@ * `Spatie\Permission\Contracts\Permission` contract. */ - 'permission' => Spatie\Permission\Models\Permission::class, + 'permission' => Permission::class, /* * When using the "HasRoles" trait from this package, we need to know which @@ -24,7 +27,7 @@ * `Spatie\Permission\Contracts\Role` contract. */ - 'role' => Spatie\Permission\Models\Role::class, + 'role' => Role::class, ], @@ -113,7 +116,7 @@ * When permissions or roles are updated the cache is flushed automatically. */ - 'expiration_time' => \DateInterval::createFromDateString('24 hours'), + 'expiration_time' => DateInterval::createFromDateString('24 hours'), /* * The cache key used to store all permissions. diff --git a/config/queue.php b/config/queue.php index 53eb2656e..4a259afcf 100644 --- a/config/queue.php +++ b/config/queue.php @@ -1,13 +1,40 @@ [ + + /* + |---------------------------------------------------------------------- + | Redis Queue Connection + |---------------------------------------------------------------------- + | + | Override the framework default `retry_after` (90s) so long-running + | archive jobs (ZipStream over many large NMR datasets) are not + | re-reserved by another worker mid-flight, which would otherwise cause + | a `MaxAttemptsExceededException` once the original attempt finishes. + | + | `retry_after` MUST exceed both the Horizon supervisor `timeout` + | (currently 9000s) and the realistic worst-case runtime of any single + | archive job. The framework default is far too low for this app. + */ + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 14400), + 'block_for' => null, + 'after_commit' => false, + ], + 'rabbitmq' => [ 'driver' => 'rabbitmq', 'queue' => env('RABBITMQ_QUEUE', 'default'), - 'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class, + 'connection' => AMQPLazyConnection::class, 'hosts' => [ [ @@ -28,7 +55,7 @@ 'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null), ], 'queue' => [ - 'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class, + 'job' => RabbitMQJob::class, ], ], diff --git a/config/ror.php b/config/ror.php new file mode 100644 index 000000000..b245b78f8 --- /dev/null +++ b/config/ror.php @@ -0,0 +1,19 @@ + env('ROR_API_URL', 'https://api.ror.org/organizations'), + + 'client_id' => env('ROR_CLIENT_ID'), + +]; diff --git a/config/sanctum.php b/config/sanctum.php index 764a82fac..b6607039b 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -1,5 +1,8 @@ [ - 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, - 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, - 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + 'authenticate_session' => AuthenticateSession::class, + 'encrypt_cookies' => EncryptCookies::class, + 'validate_csrf_token' => ValidateCsrfToken::class, ], ]; diff --git a/config/schemas.php b/config/schemas.php index ce06349bb..6bd8f7510 100644 --- a/config/schemas.php +++ b/config/schemas.php @@ -14,5 +14,7 @@ * Ontologies * */ - 'measurement_technique' => env('MEASUREMENT_TECHNIQUE', 'http://purl.obolibrary.org/obo/CHMO_0000613'), + 'ontologies' => [ + 'measurement_technique' => env('MEASUREMENT_TECHNIQUE', 'http://purl.obolibrary.org/obo/CHMO_0000613'), + ], ]; diff --git a/config/scout.php b/config/scout.php index 5c8b7d20f..a2991b466 100644 --- a/config/scout.php +++ b/config/scout.php @@ -132,6 +132,7 @@ 'meilisearch' => [ 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'), 'key' => env('MEILISEARCH_KEY', null), + 'public_key' => env('MEILISEARCH_PUBLICKEY', null), ], ]; diff --git a/config/services.php b/config/services.php index 88e0bd2e0..8c10d3aa3 100644 --- a/config/services.php +++ b/config/services.php @@ -48,12 +48,14 @@ // Allow overriding the PUG REST path if needed 'pug_rest_path' => env('PUBCHEM_PUG_PATH', '/rest/pug'), ], - 'common_chemistry' => [ - 'base_url' => env('COMMON_CHEMISTRY_URL', 'https://commonchemistry.cas.org'), - 'api_path' => env('COMMON_CHEMISTRY_API_PATH', '/api'), - ], + 'chemistry_standardize' => [ - 'url' => env('CHEMISTRY_STANDARDIZE_URL', 'https://api.cheminf.studio/latest/chem/standardize'), + 'url' => env('CHEMISTRY_STANDARDIZE_URL', 'https://api.naturalproducts.net/latest/chem/standardize'), ], + 'cas' => [ + 'provider' => env('CAS_PROVIDER', 'CAS_CommonChemistry'), + 'api_token' => env('CAS_API_TOKEN'), + 'base_url' => env('COMMON_CHEMISTRY_URL', 'https://commonchemistry.cas.org/api'), + ], ]; diff --git a/config/tags.php b/config/tags.php index 0b965495d..fbae79b70 100644 --- a/config/tags.php +++ b/config/tags.php @@ -1,5 +1,7 @@ Spatie\Tags\Tag::class, + 'tag_model' => Tag::class, ]; diff --git a/database/factories/AnnouncementFactory.php b/database/factories/AnnouncementFactory.php index ae3f7d0b4..4c04223e9 100644 --- a/database/factories/AnnouncementFactory.php +++ b/database/factories/AnnouncementFactory.php @@ -2,13 +2,14 @@ namespace Database\Factories; +use App\Models\Announcement; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Carbon; use Illuminate\Support\Str; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Announcement> + * @extends Factory */ class AnnouncementFactory extends Factory { diff --git a/database/factories/AuthorFactory.php b/database/factories/AuthorFactory.php index cd7caf98d..6d4ea3121 100644 --- a/database/factories/AuthorFactory.php +++ b/database/factories/AuthorFactory.php @@ -2,10 +2,11 @@ namespace Database\Factories; +use App\Models\Author; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Author> + * @extends Factory */ class AuthorFactory extends Factory { diff --git a/database/factories/CitationFactory.php b/database/factories/CitationFactory.php index bd0359bc5..3d075b608 100644 --- a/database/factories/CitationFactory.php +++ b/database/factories/CitationFactory.php @@ -2,10 +2,11 @@ namespace Database\Factories; +use App\Models\Citation; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Citation> + * @extends Factory */ class CitationFactory extends Factory { diff --git a/database/factories/DraftFactory.php b/database/factories/DraftFactory.php index 3d1b3c7d1..7361f9867 100644 --- a/database/factories/DraftFactory.php +++ b/database/factories/DraftFactory.php @@ -2,13 +2,14 @@ namespace Database\Factories; +use App\Models\Draft; use App\Models\Team; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Draft> + * @extends Factory */ class DraftFactory extends Factory { diff --git a/database/factories/FileSystemObjectFactory.php b/database/factories/FileSystemObjectFactory.php new file mode 100644 index 000000000..56e5e4d1f --- /dev/null +++ b/database/factories/FileSystemObjectFactory.php @@ -0,0 +1,142 @@ + + */ +class FileSystemObjectFactory extends Factory +{ + /** + * Define the model's default state. + */ + public function definition(): array + { + return [ + 'name' => $this->faker->word().'.'.$this->faker->fileExtension(), + 'uuid' => Str::uuid(), + 'slug' => $this->faker->slug(), + 'description' => $this->faker->sentence(), + 'relative_url' => '/'.$this->faker->slug().'/'.$this->faker->word(), + 'path' => $this->faker->filePath(), + 'type' => $this->faker->randomElement(['file', 'directory']), // Changed from 'folder' to 'directory' + 'key' => Str::uuid(), + 'is_public' => false, + 'is_deleted' => false, + 'is_archived' => false, + 'is_original' => true, + 'is_verified' => false, + 'is_processed' => false, + 'is_root' => false, + 'sort_order' => $this->faker->numberBetween(1, 100), + 'level' => $this->faker->numberBetween(0, 3), + 'has_children' => false, + 'file_size' => $this->faker->numberBetween(1024, 1048576), // 1KB to 1MB + 'integrity_status' => $this->faker->randomElement(['pending', 'verified', 'failed']), + 'status' => $this->faker->randomElement(['present', 'missing']), + 'checksum_md5' => md5($this->faker->text()), + 'checksum_sha256' => hash('sha256', $this->faker->text()), + 'checksum_algorithm' => 'sha256', + ]; + } + + /** + * Configure the factory for a file type. + */ + public function file(): static + { + return $this->state(fn (array $attributes) => [ + 'type' => 'file', + 'has_children' => false, + ]); + } + + /** + * Configure the factory for a directory type. + */ + public function directory(): static + { + return $this->state(fn (array $attributes) => [ + 'type' => 'directory', + 'has_children' => true, + ]); + } + + /** + * Configure the factory for a missing status. + */ + public function missing(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => 'missing', + ]); + } + + /** + * Configure the factory for a complete status. + */ + public function complete(): static + { + return $this->state(fn (array $attributes) => [ + 'status' => 'complete', + ]); + } + + /** + * Configure the factory for root level files. + */ + public function rootLevel(): static + { + return $this->state(fn (array $attributes) => [ + 'level' => 0, + 'parent_id' => null, + ]); + } + + /** + * Configure the factory for child files. + */ + public function childLevel(int $level = 1): static + { + return $this->state(fn (array $attributes) => [ + 'level' => $level, + ]); + } + + /** + * Configure the factory to belong to a draft. + */ + public function forDraft(Draft $draft): static + { + return $this->state(fn (array $attributes) => [ + 'draft_id' => $draft->id, + ]); + } + + /** + * Configure the factory to belong to a project. + */ + public function forProject(Project $project): static + { + return $this->state(fn (array $attributes) => [ + 'project_id' => $project->id, + ]); + } + + /** + * Configure the factory to belong to a study. + */ + public function forStudy(Study $study): static + { + return $this->state(fn (array $attributes) => [ + 'study_id' => $study->id, + ]); + } +} diff --git a/database/factories/LicenseFactory.php b/database/factories/LicenseFactory.php index abfd28b9c..afcdc09e8 100644 --- a/database/factories/LicenseFactory.php +++ b/database/factories/LicenseFactory.php @@ -2,29 +2,37 @@ namespace Database\Factories; +use App\Models\License; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\License> + * @extends Factory */ class LicenseFactory extends Factory { + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = License::class; + /** * Define the model's default state. */ public function definition(): array { - $title = $this->faker->sentence($nbWords = 4); + $title = fake()->sentence(4); $slug = Str::slug($title, '-'); return [ 'title' => $title, 'slug' => $slug, 'spdx_id' => Str::random(), - 'url' => $this->faker->url(), - 'description' => $this->faker->text(), - 'body' => $this->faker->text(), + 'url' => fake()->url(), + 'description' => fake()->text(), + 'body' => fake()->text(), 'category' => Str::random(40), ]; } diff --git a/database/factories/LinkedSocialAccountFactory.php b/database/factories/LinkedSocialAccountFactory.php new file mode 100644 index 000000000..04e5b8c1e --- /dev/null +++ b/database/factories/LinkedSocialAccountFactory.php @@ -0,0 +1,54 @@ + User::factory(), + 'provider_id' => $this->faker->unique()->numerify('########'), + 'provider_name' => $this->faker->randomElement(['github', 'google', 'orcid']), + ]; + } + + /** + * Indicate that the linked account is for GitHub. + */ + public function github(): static + { + return $this->state(fn (array $attributes) => [ + 'provider_name' => 'github', + ]); + } + + /** + * Indicate that the linked account is for Google. + */ + public function google(): static + { + return $this->state(fn (array $attributes) => [ + 'provider_name' => 'google', + ]); + } + + /** + * Indicate that the linked account is for ORCID. + */ + public function orcid(): static + { + return $this->state(fn (array $attributes) => [ + 'provider_name' => 'orcid', + ]); + } +} diff --git a/database/factories/MoleculeFactory.php b/database/factories/MoleculeFactory.php index b988ef894..b6c654a32 100644 --- a/database/factories/MoleculeFactory.php +++ b/database/factories/MoleculeFactory.php @@ -12,74 +12,55 @@ class MoleculeFactory extends Factory */ public function definition(): array { - $cid = rand(1000, 9999); - echo $cid; - $pubchemRecordLink = 'https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/'.$cid.'/record/JSON'; - $json = file_get_contents($pubchemRecordLink); - $data = json_decode($json, true)['PC_Compounds'][0]['props']; + // Generate fake molecule data instead of making API calls + // This prevents external API dependencies and rate limiting issues - $output = []; - $labels = [ - 'InChI' => 'standard_inchi', - 'InChIKey' => 'inchi_key', - 'Molecular Formula' => 'molecular_formula', - ]; - - foreach ($data as $key => $value) { - $pubchemLabel = $data[$key]['urn']['label']; - - foreach ($labels as $label => $column) { - if ($pubchemLabel == $label) { - $val = $data[$key]['value']['sval']; - $output[$column] = $val; - } - } + // Generate unique molecular formulas using random carbon/hydrogen/oxygen counts + $c = $this->faker->numberBetween(6, 25); + $h = $this->faker->numberBetween(8, 50); + $o = $this->faker->numberBetween(0, 10); + $molecularFormula = "C{$c}H{$h}"; + if ($o > 0) { + $molecularFormula .= "O{$o}"; } - return - [ + // Generate unique InChI and InChI key using random components + $uniqueId = $this->faker->uuid(); + $hashPart = substr(md5($uniqueId), 0, 10); + $standardInchi = "InChI=1S/{$molecularFormula}/c{$hashPart}/h{$this->faker->randomNumber(5)}"; + $inchiKey = strtoupper(substr(md5($standardInchi), 0, 14)).'-'.strtoupper(substr(md5($uniqueId), 0, 10)).'-N'; + + return [ 'cas' => null, - 'molecular_formula' => $output['molecular_formula'], - 'molecular_weight' => null, + 'molecular_formula' => $molecularFormula, + 'molecular_weight' => $this->faker->randomFloat(2, 100, 500), 'smiles' => null, 'absolute_smiles' => null, 'canonical_smiles' => null, 'inchi' => null, - 'standard_inchi' => $output['standard_inchi'], - 'inchi_key' => $output['inchi_key'], + 'standard_inchi' => $standardInchi, + 'inchi_key' => $inchiKey, 'standard_inchi_key' => null, - 'fp0' => null, - 'fp1' => null, - 'fp2' => null, - 'fp3' => null, - 'fp4' => null, - 'fp5' => null, - 'fp6' => null, - 'fp7' => null, - 'fp8' => null, - 'fp9' => null, - 'fp10' => null, - 'fp11' => null, - 'fp12' => null, - 'fp13' => null, - 'fp14' => null, - 'fp15' => null, - 'DBE' => null, - 'SSSR' => null, - 'SAR' => null, - 'COMMENT' => null, 'sdf' => null, - 'MULTIPLICITY_0' => null, - 'MULTIPLICITY_1' => null, - 'MULTIPLICITY_2' => null, - 'MULTIPLICITY_3' => null, - 'VIEWS' => null, 'DOI' => null, 'created_at' => Carbon::now()->timestamp, 'updated_at' => Carbon::now()->timestamp, 'doi' => null, 'datacite_schema' => null, - 'identifier' => null, + 'identifier' => $this->faker->unique()->numberBetween(1, 999999), // bigint identifier for searches + 'name' => $this->faker->words(2, true), + 'name_trust_level' => 0, + 'annotation_level' => 0, + 'synonyms' => null, + 'iupac_name' => null, + '2d' => null, + '3d' => null, + 'structural_comments' => null, + 'status' => 'APPROVED', + 'active' => true, + 'has_stereo' => false, + 'has_variants' => false, + 'variants_count' => 0, ]; } } diff --git a/database/factories/NMRiumFactory.php b/database/factories/NMRiumFactory.php index 714f3637d..c2237629c 100644 --- a/database/factories/NMRiumFactory.php +++ b/database/factories/NMRiumFactory.php @@ -2,10 +2,13 @@ namespace Database\Factories; +use App\Models\Dataset; +use App\Models\Model; +use App\Models\Study; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Model> + * @extends Factory */ class NMRiumFactory extends Factory { @@ -16,7 +19,30 @@ public function definition(): array { return [ 'nmrium_info' => '{}', - 'dataset_id' => 1, + 'nmriumable_id' => 1, + 'nmriumable_type' => Dataset::class, ]; } + + /** + * Make this NMRium belong to a Dataset + */ + public function forDataset($dataset = null): static + { + return $this->state(fn (array $attributes) => [ + 'nmriumable_id' => $dataset?->id ?? Dataset::factory(), + 'nmriumable_type' => Dataset::class, + ]); + } + + /** + * Make this NMRium belong to a Study + */ + public function forStudy($study = null): static + { + return $this->state(fn (array $attributes) => [ + 'nmriumable_id' => $study?->id ?? Study::factory(), + 'nmriumable_type' => Study::class, + ]); + } } diff --git a/database/factories/ProjectInvitationFactory.php b/database/factories/ProjectInvitationFactory.php new file mode 100644 index 000000000..5fc75005d --- /dev/null +++ b/database/factories/ProjectInvitationFactory.php @@ -0,0 +1,60 @@ + + */ +class ProjectInvitationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'project_id' => Project::factory(), + 'email' => fake()->unique()->safeEmail(), + 'role' => fake()->randomElement(['viewer', 'collaborator']), + 'message' => fake()->optional()->sentence(), + 'invited_by' => User::factory(), + ]; + } + + /** + * Indicate that the invitation is for a collaborator role. + */ + public function collaborator(): static + { + return $this->state(fn (array $attributes) => [ + 'role' => 'collaborator', + ]); + } + + /** + * Indicate that the invitation is for a viewer role. + */ + public function viewer(): static + { + return $this->state(fn (array $attributes) => [ + 'role' => 'viewer', + ]); + } + + /** + * Indicate that the invitation has expired. + */ + public function expired(): static + { + return $this->state(fn (array $attributes) => [ + 'created_at' => now()->subDays(8), // Assuming 7-day expiry + ]); + } +} diff --git a/database/factories/StudyFactory.php b/database/factories/StudyFactory.php index d6beca729..ee20b8d57 100644 --- a/database/factories/StudyFactory.php +++ b/database/factories/StudyFactory.php @@ -2,7 +2,6 @@ namespace Database\Factories; -use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; @@ -42,8 +41,6 @@ public function definition(): array 'fs_id' => 1, 'release_date' => null, 'study_photo_path' => null, // todo: Adjust when studies images field is provided in nmrXiv - 'created_at' => Carbon::now()->timestamp, - 'updated_at' => Carbon::now()->timestamp, 'doi' => null, 'datacite_schema' => null, 'identifier' => null, diff --git a/database/factories/StudyInvitationFactory.php b/database/factories/StudyInvitationFactory.php new file mode 100644 index 000000000..2a7be6e73 --- /dev/null +++ b/database/factories/StudyInvitationFactory.php @@ -0,0 +1,77 @@ + Study::factory(), + 'email' => $this->faker->unique()->safeEmail(), + 'role' => $this->faker->randomElement(['viewer', 'collaborator', 'reviewer']), + 'message' => $this->faker->optional()->sentence(), + 'invited_by' => $this->faker->optional()->safeEmail(), + ]; + } + + /** + * Factory state for viewer role + */ + public function viewer(): self + { + return $this->state(fn (array $attributes) => [ + 'role' => 'viewer', + ]); + } + + /** + * Factory state for collaborator role + */ + public function collaborator(): self + { + return $this->state(fn (array $attributes) => [ + 'role' => 'collaborator', + ]); + } + + /** + * Factory state for reviewer role + */ + public function reviewer(): self + { + return $this->state(fn (array $attributes) => [ + 'role' => 'reviewer', + ]); + } + + /** + * Factory state with a specific inviter + */ + public function invitedBy(User $user): self + { + return $this->state(fn (array $attributes) => [ + 'invited_by' => $user->email, + ]); + } + + /** + * Factory state for a specific study + */ + public function forStudy(Study $study): self + { + return $this->state(fn (array $attributes) => [ + 'study_id' => $study->id, + ]); + } +} diff --git a/database/factories/TeamInvitationFactory.php b/database/factories/TeamInvitationFactory.php new file mode 100644 index 000000000..1991fb438 --- /dev/null +++ b/database/factories/TeamInvitationFactory.php @@ -0,0 +1,89 @@ + + */ +class TeamInvitationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'team_id' => Team::factory(), + 'email' => fake()->unique()->safeEmail(), + 'role' => fake()->randomElement(['admin', 'editor']), + 'invited_by' => User::factory(), + ]; + } + + /** + * Indicate that the invitation is for an admin role. + */ + public function admin(): static + { + return $this->state(fn (array $attributes) => [ + 'role' => 'admin', + ]); + } + + /** + * Indicate that the invitation is for an editor role. + */ + public function editor(): static + { + return $this->state(fn (array $attributes) => [ + 'role' => 'editor', + ]); + } + + /** + * Indicate that the invitation is for an owner role. + */ + public function owner(): static + { + return $this->state(fn (array $attributes) => [ + 'role' => 'owner', + ]); + } + + /** + * Set a specific email for the invitation. + */ + public function forEmail(string $email): static + { + return $this->state(fn (array $attributes) => [ + 'email' => $email, + ]); + } + + /** + * Set a specific team for the invitation. + */ + public function forTeam(Team $team): static + { + return $this->state(fn (array $attributes) => [ + 'team_id' => $team->id, + ]); + } + + /** + * Set a specific inviter for the invitation. + */ + public function invitedBy(User $user): static + { + return $this->state(fn (array $attributes) => [ + 'invited_by' => $user->id, + ]); + } +} diff --git a/database/factories/TickerFactory.php b/database/factories/TickerFactory.php new file mode 100644 index 000000000..77b816caf --- /dev/null +++ b/database/factories/TickerFactory.php @@ -0,0 +1,81 @@ + + */ +class TickerFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = Ticker::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'type' => $this->faker->randomElement(['sample', 'molecule', 'dataset']), + 'index' => $this->faker->numberBetween(1, 1000), + 'meta' => null, + ]; + } + + /** + * Create a ticker for samples + */ + public function sample(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'type' => 'sample', + ]; + }); + } + + /** + * Create a ticker for molecules + */ + public function molecule(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'type' => 'molecule', + ]; + }); + } + + /** + * Create a ticker for datasets + */ + public function dataset(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'type' => 'dataset', + ]; + }); + } + + /** + * Set a specific index value + */ + public function withIndex(int $index): Factory + { + return $this->state(function (array $attributes) use ($index) { + return [ + 'index' => $index, + ]; + }); + } +} diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index da47b9411..04cc01776 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -21,7 +21,7 @@ public function definition(): array 'last_name' => $this->faker->lastName(), 'email' => $this->faker->unique()->safeEmail(), 'email_verified_at' => now(), - 'username' => $this->faker->userName(), + 'username' => $this->faker->unique()->userName(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; @@ -30,7 +30,7 @@ public function definition(): array /** * Indicate that the model's email address should be unverified. * - * @return \Illuminate\Database\Eloquent\Factories\Factory + * @return Factory */ public function unverified() { diff --git a/database/factories/ValidationFactory.php b/database/factories/ValidationFactory.php new file mode 100644 index 000000000..a2175b801 --- /dev/null +++ b/database/factories/ValidationFactory.php @@ -0,0 +1,94 @@ + + */ +class ValidationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'report' => [ + 'project' => [ + 'status' => fake()->boolean(), + 'title' => fake()->boolean(), + 'description' => fake()->boolean(), + 'authors' => fake()->boolean(), + 'affiliation' => fake()->boolean(), + 'license' => fake()->boolean(), + 'keywords' => fake()->boolean(), + 'studies' => [ + ['status' => fake()->boolean()], + ['status' => fake()->boolean()], + ], + ], + 'missing' => [], + 'errors' => [], + 'version' => 1, + ], + ]; + } + + /** + * Indicate that the validation has passed. + */ + public function passed(): static + { + return $this->state(fn (array $attributes) => [ + 'report' => [ + 'project' => [ + 'status' => true, + 'title' => true, + 'description' => true, + 'authors' => true, + 'affiliation' => true, + 'license' => true, + 'keywords' => true, + 'studies' => [ + ['status' => true], + ], + ], + 'missing' => [], + 'errors' => [], + 'version' => 1, + ], + ]); + } + + /** + * Indicate that the validation has failed. + */ + public function failed(): static + { + return $this->state(fn (array $attributes) => [ + 'report' => [ + 'project' => [ + 'status' => false, + 'title' => false, + 'description' => false, + 'authors' => false, + 'affiliation' => false, + 'license' => false, + 'keywords' => false, + 'studies' => [ + ['status' => false], + ['status' => false], + ], + ], + 'missing' => ['title', 'description', 'authors'], + 'errors' => ['Project validation failed'], + 'version' => 1, + ], + ]); + } +} diff --git a/database/migrations/2021_08_26_154527_create_permission_tables.php b/database/migrations/2021_08_26_154527_create_permission_tables.php index 574f58927..05287893b 100644 --- a/database/migrations/2021_08_26_154527_create_permission_tables.php +++ b/database/migrations/2021_08_26_154527_create_permission_tables.php @@ -15,7 +15,7 @@ public function up(): void $columnNames = config('permission.column_names'); if (empty($tableNames)) { - throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.'); + throw new Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.'); } Schema::create($tableNames['permissions'], function (Blueprint $table) { @@ -98,7 +98,7 @@ 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.'); + 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']); diff --git a/database/migrations/2023_08_25_111326_update_nmrium_table.php b/database/migrations/2023_08_25_111326_update_nmrium_table.php index 045c5cb60..e10cfb156 100644 --- a/database/migrations/2023_08_25_111326_update_nmrium_table.php +++ b/database/migrations/2023_08_25_111326_update_nmrium_table.php @@ -1,5 +1,6 @@ renameColumn('dataset_id', 'nmriumable_id'); - $table->string('nmriumable_type')->default(\App\Models\Dataset::class); + $table->string('nmriumable_type')->default(Dataset::class); }); Schema::table('studies', function (Blueprint $table) { diff --git a/database/migrations/2025_12_11_115948_add_ror_id_to_users_table.php b/database/migrations/2025_12_11_115948_add_ror_id_to_users_table.php new file mode 100644 index 000000000..0b3046f98 --- /dev/null +++ b/database/migrations/2025_12_11_115948_add_ror_id_to_users_table.php @@ -0,0 +1,28 @@ +string('ror_id')->nullable()->after('affiliation'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('ror_id'); + }); + } +}; diff --git a/database/migrations/2025_12_15_105635_change_affiliation_to_text_in_users_table.php b/database/migrations/2025_12_15_105635_change_affiliation_to_text_in_users_table.php new file mode 100644 index 000000000..190111062 --- /dev/null +++ b/database/migrations/2025_12_15_105635_change_affiliation_to_text_in_users_table.php @@ -0,0 +1,28 @@ +text('affiliation')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->string('affiliation')->nullable()->change(); + }); + } +}; diff --git a/database/migrations/2025_12_15_115641_add_ror_id_to_authors_table.php b/database/migrations/2025_12_15_115641_add_ror_id_to_authors_table.php new file mode 100644 index 000000000..29b667b22 --- /dev/null +++ b/database/migrations/2025_12_15_115641_add_ror_id_to_authors_table.php @@ -0,0 +1,28 @@ +string('ror_id')->nullable()->after('affiliation'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('authors', function (Blueprint $table) { + $table->dropColumn('ror_id'); + }); + } +}; diff --git a/database/migrations/2026_02_09_091118_create_author_study_table.php b/database/migrations/2026_02_09_091118_create_author_study_table.php new file mode 100644 index 000000000..84efddf6d --- /dev/null +++ b/database/migrations/2026_02_09_091118_create_author_study_table.php @@ -0,0 +1,38 @@ +id(); + $table->unsignedBigInteger('author_id'); + $table->unsignedBigInteger('study_id'); + $table->string('contributor_type')->nullable(); + $table->smallInteger('sort_order')->nullable(); + $table->timestamps(); + + $table->foreign('author_id')->references('id')->on('authors')->onDelete('cascade'); + $table->foreign('study_id')->references('id')->on('studies')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('author_study'); + } +}; diff --git a/database/migrations/2026_02_19_120638_add_metadata_extraction_bagit_generation_columns_to_studies_table.php b/database/migrations/2026_02_19_120638_add_metadata_extraction_bagit_generation_columns_to_studies_table.php new file mode 100644 index 000000000..d9f1e450f --- /dev/null +++ b/database/migrations/2026_02_19_120638_add_metadata_extraction_bagit_generation_columns_to_studies_table.php @@ -0,0 +1,29 @@ +string('metadata_bagit_generation_status')->nullable()->after('is_public'); + $table->json('metadata_bagit_generation_logs')->nullable()->after('metadata_bagit_generation_status'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('studies', function (Blueprint $table) { + $table->dropColumn(['metadata_bagit_generation_status', 'metadata_bagit_generation_logs']); + }); + } +}; diff --git a/database/migrations/2026_04_01_060726_add_title_slug_to_citations_table.php b/database/migrations/2026_04_01_060726_add_title_slug_to_citations_table.php new file mode 100644 index 000000000..5a9028c09 --- /dev/null +++ b/database/migrations/2026_04_01_060726_add_title_slug_to_citations_table.php @@ -0,0 +1,44 @@ +string('title_slug')->nullable()->after('title')->index(); + }); + + DB::table('citations') + ->select('id', 'title') + ->orderBy('id') + ->chunkById(500, function ($citations): void { + foreach ($citations as $citation) { + DB::table('citations') + ->where('id', $citation->id) + ->update([ + 'title_slug' => Str::slug((string) $citation->title), + ]); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('citations', function (Blueprint $table) { + $table->dropIndex(['title_slug']); + $table->dropColumn('title_slug'); + }); + } +}; diff --git a/database/migrations/2026_05_04_182654_add_provisional_doi_to_projects_table.php b/database/migrations/2026_05_04_182654_add_provisional_doi_to_projects_table.php new file mode 100644 index 000000000..3ef87d1d5 --- /dev/null +++ b/database/migrations/2026_05_04_182654_add_provisional_doi_to_projects_table.php @@ -0,0 +1,28 @@ +string('provisional_doi', 255)->nullable()->after('doi'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('projects', function (Blueprint $table) { + $table->dropColumn('provisional_doi'); + }); + } +}; diff --git a/database/migrations/2026_05_06_145802_add_assignments_to_datasets_table.php b/database/migrations/2026_05_06_145802_add_assignments_to_datasets_table.php new file mode 100644 index 000000000..ca3b73be8 --- /dev/null +++ b/database/migrations/2026_05_06_145802_add_assignments_to_datasets_table.php @@ -0,0 +1,37 @@ +", + * "atom_peaks": [ { "atom": "C1", "peak": 7.42, "label": "..." }, ... ], + * "source": "manual" | "nmrium", + * "updated_at": "ISO-8601 timestamp" + * } + * + * Stored on the dataset (not in a separate table) because each spectrum + * has at most one assignment block and the data is always read together + * with the dataset. Nullable so existing rows stay untouched. + */ + public function up(): void + { + Schema::table('datasets', function (Blueprint $table) { + $table->json('assignments')->nullable()->after('description'); + }); + } + + public function down(): void + { + Schema::table('datasets', function (Blueprint $table) { + $table->dropColumn('assignments'); + }); + } +}; diff --git a/database/migrations/2026_05_07_220000_add_provisional_doi_registered_at_to_projects_table.php b/database/migrations/2026_05_07_220000_add_provisional_doi_registered_at_to_projects_table.php new file mode 100644 index 000000000..3e9e9e03e --- /dev/null +++ b/database/migrations/2026_05_07_220000_add_provisional_doi_registered_at_to_projects_table.php @@ -0,0 +1,33 @@ +timestamp('provisional_doi_registered_at')->nullable()->after('provisional_doi'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('projects', function (Blueprint $table) { + $table->dropColumn('provisional_doi_registered_at'); + }); + } +}; diff --git a/database/migrations/2026_05_18_120000_create_citation_study_table.php b/database/migrations/2026_05_18_120000_create_citation_study_table.php new file mode 100644 index 000000000..84ecdd236 --- /dev/null +++ b/database/migrations/2026_05_18_120000_create_citation_study_table.php @@ -0,0 +1,25 @@ +id(); + $table->foreignId('study_id'); + $table->foreignId('citation_id'); + $table->string('user')->nullable(); + $table->timestamps(); + $table->unique(['study_id', 'citation_id']); + }); + } + + public function down(): void + { + Schema::dropIfExists('citation_study'); + } +}; diff --git a/database/migrations/2026_05_21_111309_create_embargo_reminders_table.php b/database/migrations/2026_05_21_111309_create_embargo_reminders_table.php new file mode 100644 index 000000000..9d3d2f698 --- /dev/null +++ b/database/migrations/2026_05_21_111309_create_embargo_reminders_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('project_id')->constrained()->onDelete('cascade'); + $table->integer('days_before_release'); // 7, 3, or 1 + $table->timestamp('sent_at'); + $table->timestamps(); + + $table->unique(['project_id', 'days_before_release']); + $table->index(['project_id', 'days_before_release']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('embargo_reminders'); + } +}; diff --git a/database/seeders/RolesAndPermissionsSeeder.php b/database/seeders/RolesAndPermissionsSeeder.php index 18a5680f1..76e9798f7 100644 --- a/database/seeders/RolesAndPermissionsSeeder.php +++ b/database/seeders/RolesAndPermissionsSeeder.php @@ -5,6 +5,7 @@ use Illuminate\Database\Seeder; use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Role; +use Spatie\Permission\PermissionRegistrar; class RolesAndPermissionsSeeder extends Seeder { @@ -14,7 +15,7 @@ class RolesAndPermissionsSeeder extends Seeder public function run(): void { // Reset cached roles and permissions - app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + app()[PermissionRegistrar::class]->forgetCachedPermissions(); // create permissions Permission::create(['name' => 'view studies']); diff --git a/deployment/Dockerfile b/deployment/Dockerfile index 7c5222c67..013fb1fa5 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -1,4 +1,4 @@ -FROM dunglas/frankenphp:1.7.0-alpine +FROM dunglas/frankenphp:1-php8.4-alpine # Setup proxy settings using build arguments ARG HTTP_PROXY @@ -44,7 +44,7 @@ COPY deployment/php.production.ini /usr/local/etc/php/conf.d/production.ini COPY composer.json composer.lock package*.json ./ # Install PHP dependencies -RUN composer install --no-dev --optimize-autoloader --no-interaction --no-progress --no-scripts +RUN composer install --optimize-autoloader --no-interaction --no-progress --no-scripts # Install Node dependencies RUN npm ci --production=false @@ -77,6 +77,10 @@ COPY deployment/Caddyfile /etc/caddy/Caddyfile RUN php artisan l5-swagger:generate +# Add healthcheck - use Laravel health endpoint for broader compatibility +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost/up || exit 1 + # Expose port EXPOSE 80 443 diff --git a/deployment/Dockerfile.worker b/deployment/Dockerfile.worker index fd15d46f8..2d434510a 100644 --- a/deployment/Dockerfile.worker +++ b/deployment/Dockerfile.worker @@ -1,4 +1,4 @@ -FROM php:8.3-alpine +FROM dunglas/frankenphp:1-php8.4-alpine # Setup proxy settings using build arguments ARG HTTP_PROXY @@ -23,8 +23,7 @@ RUN /sbin/apk add --no-cache \ oniguruma-dev \ postgresql-dev \ zlib-dev \ - supervisor \ - $PHPIZE_DEPS + supervisor # Install PostgreSQL client RUN /sbin/apk --update add postgresql17-client --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main @@ -32,31 +31,12 @@ RUN /sbin/apk --update add postgresql17-client --repository=https://dl-cdn.alpin # Install PHP extensions RUN docker-php-ext-install pdo pdo_pgsql mbstring exif pcntl bcmath gd intl zip -# Install APCu and Redis PHP extensions from source -RUN cd /tmp && \ - # Download APCu from GitHub - curl -L -o apcu.tar.gz https://github.com/krakjoe/apcu/archive/refs/tags/v5.1.22.tar.gz && \ - mkdir -p /tmp/apcu && \ - tar -xf apcu.tar.gz -C /tmp/apcu --strip-components=1 && \ - cd /tmp/apcu && \ - phpize && \ - ./configure && \ - make && \ - make install && \ - # Download Redis from GitHub - cd /tmp && \ - curl -L -o redis.tar.gz https://github.com/phpredis/phpredis/archive/refs/tags/5.3.7.tar.gz && \ - mkdir -p /tmp/redis && \ - tar -xf redis.tar.gz -C /tmp/redis --strip-components=1 && \ - cd /tmp/redis && \ - phpize && \ - ./configure && \ - make && \ - make install && \ - # Enable extensions +# Install APCu and Redis via PECL with build dependencies added and removed in same layer +RUN /sbin/apk add --no-cache --virtual .build-deps autoconf gcc g++ make && \ + pecl install apcu redis && \ docker-php-ext-enable apcu redis && \ - # Clean up - rm -rf /tmp/apcu /tmp/redis /tmp/apcu.tar.gz /tmp/redis.tar.gz + /sbin/apk del .build-deps && \ + rm -rf /tmp/pear # Configure PHP COPY deployment/php.production.ini /usr/local/etc/php/conf.d/production.ini @@ -86,5 +66,9 @@ COPY deployment/supervisord.worker.conf /etc/supervisor/conf.d/supervisord.conf # Create required directories for Supervisor RUN mkdir -p /var/log/supervisor /run/supervisor +# Add healthcheck - verify Horizon process is running +HEALTHCHECK --interval=10s --timeout=3s --start-period=30s --retries=3 \ + CMD pgrep -f "artisan horizon" || exit 1 + # Start Supervisord CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 286dc455d..55538546a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,11 @@ services: laravel.test: build: - context: ./docker/8.3 + context: ./docker/8.4 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' - image: sail-8.3/app + image: sail-8.4/app extra_hosts: - 'host.docker.internal:host-gateway' ports: diff --git a/docker/8.4/Dockerfile b/docker/8.4/Dockerfile new file mode 100644 index 000000000..f647912ff --- /dev/null +++ b/docker/8.4/Dockerfile @@ -0,0 +1,64 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=20 +ARG POSTGRES_VERSION=15 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND noninteractive +ENV TZ=UTC +ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update \ + && mkdir -p /etc/apt/keyrings \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch \ + && 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.4-cli php8.4-dev \ + php8.4-pgsql php8.4-sqlite3 php8.4-gd \ + php8.4-curl \ + php8.4-imap php8.4-mysql php8.4-mbstring \ + php8.4-xml php8.4-zip php8.4-bcmath php8.4-soap \ + php8.4-intl php8.4-readline \ + php8.4-ldap \ + php8.4-msgpack php8.4-igbinary php8.4-redis php8.4-swoole \ + php8.4-memcached php8.4-pcov php8.4-imagick php8.4-xdebug \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && npm install -g pnpm \ + && npm install -g bun \ + && 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.4 + +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.4/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 8000 + +ENTRYPOINT ["start-container"] diff --git a/docker/8.4/php.ini b/docker/8.4/php.ini new file mode 100644 index 000000000..26dbdf6e6 --- /dev/null +++ b/docker/8.4/php.ini @@ -0,0 +1,10 @@ +[PHP] +post_max_size = 2G +upload_max_filesize = 2G +memory_limit = 2G +max_execution_time = 259200 +max_input_time = 259200 +variables_order = EGPCS + +[opcache] +opcache.enable_cli=1 diff --git a/docker/8.4/start-container b/docker/8.4/start-container new file mode 100644 index 000000000..b86439907 --- /dev/null +++ b/docker/8.4/start-container @@ -0,0 +1,17 @@ +#!/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.4/supervisord.conf b/docker/8.4/supervisord.conf new file mode 100644 index 000000000..26ccc0a50 --- /dev/null +++ b/docker/8.4/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=%(ENV_SUPERVISOR_PHP_COMMAND)s +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/docs/developer-guides/csp-summary.md b/docs/developer-guides/csp-summary.md new file mode 100644 index 000000000..aecc93a46 --- /dev/null +++ b/docs/developer-guides/csp-summary.md @@ -0,0 +1,113 @@ +# nmrXiv Content Security Policy (CSP) summary + +This guide summarizes the CSP implementation in nmrXiv, the key changes we made, and how to work with it in development and production. + +## What changed + +- Introduced a custom CSP preset: `App/Support/Csp/Policies/NmrxivPolicy.php` (Spatie preset). +- Configured CSP in `config/csp.php` with support for both enforcement and report-only modes. +- Added a violation reporting endpoint (POST) and a viewer (GET) at `/csp-violation-report`. +- Updated templates to cooperate with CSP (nonces in production, permissive inline in development). +- Fixed third-party allowances (Matomo, Bunny Fonts) and dev tooling (Vite/HMR, IPv6 `localhost`). + +## Files involved + +- `app/Support/Csp/Policies/NmrxivPolicy.php` + - Central, environment-aware policy. + - Production: nonces are used; no `unsafe-inline`/`unsafe-eval`. + - Development/local: nonces are disabled; `unsafe-inline`/`unsafe-eval` permitted for DX and Vite HMR. + - Explicitly allows required third-party and dev sources (see below). + +- `config/csp.php` + - Switch between enforcement and report-only by moving the policy class between `presets` and `report_only_presets`. + - Controls `enabled`, `report_uri`, nonce generator, and whether to enable during hot reloading. + +- `routes/web.php` + - `POST /csp-violation-report` → receives reports (throttled). + - `GET /csp-violation-report` → view latest parsed violations. + +- `app/Http/Controllers/CspViolationController.php` + - Logs violation payloads and returns recent entries for inspection. + +- `resources/views/app.blade.php` + - Uses `@cspNonce` where needed in production (e.g., analytics). + - In development, policy disables nonces to avoid the browser ignoring `unsafe-inline`. + +## Effective directives (overview) + +Always applied (with environment-specific additions): +- `base-uri 'self'` +- `default-src 'self'` +- `object-src 'none'` +- `frame-src 'self'` +- `form-action 'self'` +- `img-src 'self' data: blob:` (+ localhost allowances in dev) +- `media-src 'self' blob:` +- `font-src data: https://fonts.bunny.net` +- `style-src 'self' data: https://fonts.bunny.net` (behavior differs by env) +- `script-src 'self' https://matomo.nfdi4chem.de` (behavior differs by env) +- `connect-src 'self' https://matomo.nfdi4chem.de https://fonts.bunny.net` (+ dev sockets/hosts) +- `report-uri /csp-violation-report` + +### Production (hardened) +- Nonces enabled (generated by `Spatie\Csp\Nonce\RandomString`). + - `script-src 'self' 'nonce-…' …` + - `style-src 'self' 'nonce-…' …` +- No `unsafe-inline` and no `unsafe-eval`. +- Third‑party: `https://matomo.nfdi4chem.de`, `https://fonts.bunny.net`. + +### Development/local (DX-friendly) +- Nonces disabled to allow inline execution where needed by tooling. +- Allows: + - `script-src … 'unsafe-inline' 'unsafe-eval'` + - `style-src … 'unsafe-inline'` +- Vite/HMR and dev connections (IPv4, named, and IPv6): + - Scripts: `http://localhost:5173`, `http://127.0.0.1:5173`, `http://[::1]:5173` (also 3000, 8000 variants) + - WebSockets: `ws://localhost:5173`, `ws://127.0.0.1:5173`, `ws://[::1]:5173` (also 3000 variants) + - HTTP connects for the same hosts/ports. +- Images additionally allow `http(s)://localhost:*`. + +## Third‑party notes +- Matomo: use explicit `https://matomo.nfdi4chem.de` (protocol‑relative sources like `//…` are invalid in CSP). +- Fonts: `https://fonts.bunny.net` allowed for `font-src` and `style-src`. + +## Violation reporting +- POST reports to: `/csp-violation-report` (throttled; can be relaxed locally if noisy). +- GET: `/csp-violation-report` returns a recent, parsed list for quick inspection. + +## Switching modes +- Enforcement: put `App\Support\Csp\Policies\NmrxivPolicy::class` into `presets` and remove it from `report_only_presets` in `config/csp.php`. +- Report‑only: place it in `report_only_presets` and clear from `presets`. +- Apply changes: + +```bash +php artisan config:clear +``` + +## Verifying the header + +```bash +curl -I http://localhost/ | grep -i "content-security-policy" +``` +- Development: expect `unsafe-inline`/`unsafe-eval` and Vite endpoints (including IPv6 `[::1]`). +- Production: expect `nonce-…` values; no `unsafe-inline`/`unsafe-eval`. + +## Troubleshooting +- “unsafe-inline is ignored when a hash or nonce is present” + - Expected: browsers ignore `unsafe-inline` when nonces/hashes are present. We avoid this in dev by disabling nonces there. +- “Refused to load script http://[::1]:5173/…” + - Ensure IPv6 dev hosts are included in both `script-src` and `connect-src` (policy does this). +- Many 429s to `/csp-violation-report` + - Temporarily relax throttle for development or disable reporting while testing. +- `browser-sync-client.js net::ERR_CONNECTION_REFUSED` + - Not CSP. Start BrowserSync or remove the script include locally. + +## Edit locations +- Policy: `app/Support/Csp/Policies/NmrxivPolicy.php` +- Config: `config/csp.php` +- Routes: `routes/web.php` +- Violation controller: `app/Http/Controllers/CspViolationController.php` +- App shell: `resources/views/app.blade.php` + +— +If you must allow new third‑party origins, add the narrowest possible directive for the exact origin and protocol. Avoid `unsafe-*` in production; prefer nonces and explicit source lists. diff --git a/lang/en/auth.php b/lang/en/auth.php new file mode 100644 index 000000000..d458da138 --- /dev/null +++ b/lang/en/auth.php @@ -0,0 +1,20 @@ + 'These credentials do not match our records.', + 'password' => 'These credentials do not match our records.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + +]; diff --git a/lang/en/passwords.php b/lang/en/passwords.php new file mode 100644 index 000000000..5704d616e --- /dev/null +++ b/lang/en/passwords.php @@ -0,0 +1,23 @@ + 'Your password has been reset.', + 'sent' => 'If an account exists with this email, you will receive a password reset link.', + 'throttled' => 'Please wait before retrying.', + 'token' => 'This password reset token is invalid.', + 'user' => 'If an account exists with this email, you will receive a password reset link.', + +]; diff --git a/package-lock.json b/package-lock.json index 5632976af..a8ac6db53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "nmrxiv", "dependencies": { "@headlessui/vue": "^1.6.2", "@heroicons/vue": "^2.0.11", @@ -24,9 +23,10 @@ "medium-zoom": "^1.0.8", "mitt": "^3.0.0", "ontology-elements": "^0.2.3", - "openchemlib": "^7.4.3", + "openchemlib": "^9.22.1", "pluralize": "^8.0.0", "popper.js": "^1.16.1", + "qs": "^6.14.2", "vue-instantsearch": "^4.3.3", "vue-simple-context-menu": "^4.0.4", "vue3-clipboard": "^1.0.0", @@ -46,8 +46,10 @@ "eslint": "^8.21.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-vue": "^9.3.0", + "husky": "^9.1.7", "laravel-vapor": "^0.7.1", "laravel-vite-plugin": "^1.2.0", + "lint-staged": "^16.2.7", "lodash": "^4.17.19", "postcss": "^8.4.19", "postcss-import": "^16.1.0", @@ -66,6 +68,21 @@ "integrity": "sha512-LFF/6y6h5mfwhnJaWqqxuC8zzDaHCG62kMRkd8xhDtq62TQj9dM17A9DhE87W7DhiARJsHLgcina/9P4eNCN1w==", "license": "MIT" }, + "node_modules/@algolia/abtesting": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.18.1.tgz", + "integrity": "sha512-aehCadlWOGvrT91KUIZpC0MbB8KBW9yUuvTJFd2xesR7le/IsT4nJUnjCCZ4ZqZCeTcPHPV5mo//fZ5oxcSVYw==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/autocomplete-core": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", @@ -116,99 +133,99 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.33.0.tgz", - "integrity": "sha512-Pyv+iHkkq7BJWFKzdrXm/JSbcTGvrGqJnIMwHYYlKDjuEBWhYt/z4WDLP9MbFZ9cTKb4qe8OvzEmS/0ERW3ibg==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.52.1.tgz", + "integrity": "sha512-HmXOGBOAOJPounpBzBpuY0zDYeiCpxgHnQmuA7JO6ScukcBdGp3/XM9zJk5pJx/xNGD68mbPGXWpDxGtl6BwDQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.33.0.tgz", - "integrity": "sha512-qkRc7ovjWQQJng6U1yM5esLPNDB0leGCaOh3FEfeWRyLB0xnjLsBEUkKanYq9GrewPvi17l78nDhkqB2SYzTCw==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.52.1.tgz", + "integrity": "sha512-5oo4+I8iixie9vXhCyNFCzeIr8pqA3FQ//VsLHTDvZAV4ttYOPGvYHGQq5NSalrLx5Jc3dRro/5uDOlnUMcBJg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.33.0.tgz", - "integrity": "sha512-Gq8Z4Fv0DkqDkf/bZl7ZwIF7PSCnRFwpyQoNDnUg+s4SwerXx6VwZJlIx/t5b9+l7vwWsjnKVivCfM4Ab5gw+g==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.52.1.tgz", + "integrity": "sha512-qCDoZfx5MpX7XQzvQ3bC4tSEMkQWQMaF/ABtLuoze03Y/flR563CCSws02qIJ23oX7lxl92LsilZjINVyTdtLw==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.33.0.tgz", - "integrity": "sha512-/tp1oWD3lpSXhAC4n8j0GMDbmN6pd+pATeO1GeURAFP5TVF+2Jz+NbQ1et0uCTzdazOfjEjSIv0fQSLo7bqSgA==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.52.1.tgz", + "integrity": "sha512-hnGs0/lsFJ2PWDxNBz7pxreXo/Xz7gxYRcfePBUjsH26ad0kU/sgnVZd9LwWBpsQv65z2jlb5dkyaB9WE9M9FQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.33.0.tgz", - "integrity": "sha512-hZNSqe2BXkrBQ04t5NSlqsNl4u0QrFfhXHbjO5iZ14TWt5jyOdtFMBxF3Qc0o0sqTVYnFIp0xtUbEi+/HkGeyQ==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.52.1.tgz", + "integrity": "sha512-2VxxNc/uBysyKvGeBdSM5n9eIDKH8kWD7wd9/yqbJAiVwU4Yv6tU1LSJusHKrXV/aCu1KW7t9Gug9QyeEmtn/Q==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.33.0.tgz", - "integrity": "sha512-kpu2hCIR+848T0lcf3W1GCMe+HQp/LcHceIglA6Dyw6i+y9wH3w8kmXqIV2Svv6JQ9ojEqIL8Knk7NEvD3xIBg==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.52.1.tgz", + "integrity": "sha512-O6mPtsw3xEfNOe6gWFpYLeAZAIljNa4Hgna3bq15PwyN7nbjTY0wXJFRbzs/0YVf75Br+SbOQUmjKxXYjDiSiQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.33.0.tgz", - "integrity": "sha512-Z5SAqPLxF8KyE9YPO4tAdHrXyb87DUJ0lXhFrcrG+dl/AQT9nqycQhtqDqdcQnfZrj02PImSWZQpxQj34nGZKw==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.52.1.tgz", + "integrity": "sha512-gA8oJOV1LnQQkDf91iebNnFInHuW0gRPEgLSOQ7EfipCEjYTHm5swm1DlH9H5RaRw4RrHuzHBegnlzc0MAstcg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" @@ -221,81 +238,81 @@ "license": "MIT" }, "node_modules/@algolia/ingestion": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.33.0.tgz", - "integrity": "sha512-KNJI60N+twnDLiIY+oGO2Q+syS+yBNOmNdhsB5vCzzrhi3CYs+bufnJ67/BUUfnt+T5+3VlnkvUgDkGBmmZXmA==", + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.52.1.tgz", + "integrity": "sha512-U9zZfc5xIu9wRxZkt+HceJUAD4VKHKbAyLSloJdEyMRmphXeibfrY9cxqIXBcmPeZzGhn3Imb35Dq8l19PkJhw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.33.0.tgz", - "integrity": "sha512-47R0kMDTSj8Q7rCUgIRv5Xc518tCBBS0KIZ5oRKg+hspQaJmEO+fxwGLrIIwp5JiaK6y+5sbS7bhtaajelJhpg==", + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.52.1.tgz", + "integrity": "sha512-a3SGNceHmkQfq77iG8Ka+w1pvwfZa/0lzEIgse30fL0kD+yKnd/dg0dQvSfFPAEt2f21DMcGkDSSeJlO3KdQjQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.33.0.tgz", - "integrity": "sha512-HpeLoVQuv5kW9xL0RSq1exa8ueNwyx+9B02dzFonlQzKTaSedM0jiWo6m3nWpi1hChAKqjzkL40FkxrgyrWTSg==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.52.1.tgz", + "integrity": "sha512-z98QEguCFDpxb4S/PyrUK1igqF8tPsdbqOUUO6ON91vJ58w+Gwa6ncrI0oNXSFcrkxA5EqPKPQ2A1PBCn08TYQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.33.0.tgz", - "integrity": "sha512-uOqDkvY7s9c9rkaZ4+n69LkTmZ5ax3el+8u6ipvODfj1P3HzrGvMUVFy/nGSXxw+XITKcIRphPQcyqn15b02dA==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.52.1.tgz", + "integrity": "sha512-CI7+/0I11QeZM59Uc8whd2or0kqzFVjpaPn9Qpwll/krHcBAxk24WkAQ6WX+IwDVMfpont4YGbKwAmCre3vE8Q==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0" + "@algolia/client-common": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.33.0.tgz", - "integrity": "sha512-NzTEGjwjPhUXPsrjj9nXM43+jtBVeL6UgGNBTQKsxjpqJ3EEAQ2Kq5g7DRK6mVDTQiTBWvBLKChJpn4qxwtLsg==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.52.1.tgz", + "integrity": "sha512-S6bDuw9byfOvm3T71cgdoZgrgnZq6hpdMLkx52Louh57nUAmvGQESz2aojOynQHjbTiV55smvAFbgn0qT4tJrg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0" + "@algolia/client-common": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.33.0.tgz", - "integrity": "sha512-FhEE19ScAYuXL3VLj2I3KhL7683gZwZoa+BQZUEnA05vSbVBhCAqUBQgiVu7j2RF3VceqLX3+GEeY0bHs4y7eA==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.52.1.tgz", + "integrity": "sha512-tqZXM+54rWo4mk5jL5Z/flE11nPmNEdXwFBM5py9DkOmbjeCNemfVd45FyM97XdzfZ0dl9uOJC6PYn1FpkeyQg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.33.0" + "@algolia/client-common": "5.52.1" }, "engines": { "node": ">= 14.0.0" @@ -323,21 +340,21 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -347,22 +364,22 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -420,9 +437,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", "cpu": [ "ppc64" ], @@ -437,9 +454,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", "cpu": [ "arm" ], @@ -454,9 +471,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", "cpu": [ "arm64" ], @@ -471,9 +488,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", "cpu": [ "x64" ], @@ -488,9 +505,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], @@ -505,9 +522,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", "cpu": [ "x64" ], @@ -522,9 +539,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", "cpu": [ "arm64" ], @@ -539,9 +556,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", "cpu": [ "x64" ], @@ -556,9 +573,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", "cpu": [ "arm" ], @@ -573,9 +590,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", "cpu": [ "arm64" ], @@ -590,9 +607,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", "cpu": [ "ia32" ], @@ -607,9 +624,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", "cpu": [ "loong64" ], @@ -624,9 +641,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", "cpu": [ "mips64el" ], @@ -641,9 +658,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", "cpu": [ "ppc64" ], @@ -658,9 +675,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", "cpu": [ "riscv64" ], @@ -675,9 +692,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", "cpu": [ "s390x" ], @@ -692,9 +709,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], @@ -709,9 +726,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", "cpu": [ "arm64" ], @@ -726,9 +743,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", "cpu": [ "x64" ], @@ -743,9 +760,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", "cpu": [ "arm64" ], @@ -760,9 +777,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", "cpu": [ "x64" ], @@ -776,27 +793,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", "cpu": [ "x64" ], @@ -811,9 +811,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", "cpu": [ "arm64" ], @@ -828,9 +828,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", "cpu": [ "ia32" ], @@ -845,9 +845,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", "cpu": [ "x64" ], @@ -862,9 +862,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -881,9 +881,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -987,9 +987,9 @@ "license": "BSD-3-Clause" }, "node_modules/@iconify-json/simple-icons": { - "version": "1.2.52", - "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.52.tgz", - "integrity": "sha512-c41YOMzBhl3hp58WJLxT+Qq3UhBd8GZAMkbS8ddlCuIGLW0COGe2YSfOA2+poA8/bxLhUQODRNjAy3KhiAOtzA==", + "version": "1.2.83", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.83.tgz", + "integrity": "sha512-6Pp9V++XisT9RKH7FB4RLPqUDzcmLtSma0ovOEIoEWGrXtHwBFsH7oN1z8vvCVCb95fb87QgR46/zRLyN9Y3kg==", "dev": true, "license": "CC0-1.0", "dependencies": { @@ -1029,106 +1029,10 @@ "vue": "^3.0.0" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1145,9 +1049,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", - "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, "license": "MIT", "peer": true, @@ -1157,15 +1061,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1216,16 +1120,6 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -1237,9 +1131,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", "cpu": [ "arm" ], @@ -1251,9 +1145,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", "cpu": [ "arm64" ], @@ -1265,9 +1159,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", "cpu": [ "arm64" ], @@ -1279,9 +1173,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", "cpu": [ "x64" ], @@ -1293,9 +1187,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", "cpu": [ "arm64" ], @@ -1307,9 +1201,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", "cpu": [ "x64" ], @@ -1321,9 +1215,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", "cpu": [ "arm" ], @@ -1335,9 +1229,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", "cpu": [ "arm" ], @@ -1349,9 +1243,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", "cpu": [ "arm64" ], @@ -1363,9 +1257,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", "cpu": [ "arm64" ], @@ -1376,10 +1270,24 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", "cpu": [ "loong64" ], @@ -1390,10 +1298,24 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", "cpu": [ "ppc64" ], @@ -1405,9 +1327,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", "cpu": [ "riscv64" ], @@ -1419,9 +1341,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", "cpu": [ "riscv64" ], @@ -1433,9 +1355,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", "cpu": [ "s390x" ], @@ -1447,9 +1369,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", "cpu": [ "x64" ], @@ -1461,9 +1383,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", "cpu": [ "x64" ], @@ -1474,10 +1396,38 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", "cpu": [ "arm64" ], @@ -1489,9 +1439,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", "cpu": [ "ia32" ], @@ -1502,10 +1452,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", "cpu": [ "x64" ], @@ -1622,9 +1586,9 @@ "license": "MIT" }, "node_modules/@tailwindcss/forms": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", - "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.11.tgz", + "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==", "dev": true, "license": "MIT", "dependencies": { @@ -1644,15 +1608,12 @@ } }, "node_modules/@tailwindcss/typography": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", - "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", "dev": true, "license": "MIT", "dependencies": { - "lodash.castarray": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { @@ -1660,9 +1621,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.13.12", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", - "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.14.0.tgz", + "integrity": "sha512-JLANqGy/D6k4Ujmh8Tr25lGimuOXNiaVyXaCAZS0W+1390sADdGnyUdSWNIfd49gebtIxGMij4IktRVzrdr12Q==", "license": "MIT", "funding": { "type": "github", @@ -1670,12 +1631,12 @@ } }, "node_modules/@tanstack/vue-virtual": { - "version": "3.13.12", - "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz", - "integrity": "sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==", + "version": "3.13.24", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.24.tgz", + "integrity": "sha512-A0k2qF0zFSUStXSZkGXABouXr2Tw2Ztl/cVIYG9qy84uR8W7UNjAcX3DvzBS3YnDcwvLxab8v7dbmYBZ39itDA==", "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.13.12" + "@tanstack/virtual-core": "3.14.0" }, "funding": { "type": "github", @@ -1691,30 +1652,6 @@ "integrity": "sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw==", "license": "MIT" }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1723,9 +1660,9 @@ "license": "MIT" }, "node_modules/@types/google.maps": { - "version": "3.58.1", - "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.58.1.tgz", - "integrity": "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==", + "version": "3.64.1", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.64.1.tgz", + "integrity": "sha512-nEBoa6iDNipICtxJ5VlrOgPNZQ6ixIg5nuv8iryFj0Z/1NLgxyg3pQCVegPuCzGCyTQwQI/N3uZvLUysqAzaaw==", "license": "MIT" }, "node_modules/@types/hast": { @@ -1794,20 +1731,20 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", - "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "undici-types": "~7.8.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", "license": "MIT" }, "node_modules/@types/trusted-types": { @@ -1831,9 +1768,9 @@ "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", "dev": true, "license": "ISC" }, @@ -1861,73 +1798,73 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz", - "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.34.tgz", + "integrity": "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.5", - "@vue/shared": "3.5.17", - "entities": "^4.5.0", + "@babel/parser": "^7.29.3", + "@vue/shared": "3.5.34", + "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz", - "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.34.tgz", + "integrity": "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-core": "3.5.34", + "@vue/shared": "3.5.34" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz", - "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz", + "integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.5", - "@vue/compiler-core": "3.5.17", - "@vue/compiler-dom": "3.5.17", - "@vue/compiler-ssr": "3.5.17", - "@vue/shared": "3.5.17", + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.34", + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34", "estree-walker": "^2.0.2", - "magic-string": "^0.30.17", - "postcss": "^8.5.6", + "magic-string": "^0.30.21", + "postcss": "^8.5.14", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz", - "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.34.tgz", + "integrity": "sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-dom": "3.5.34", + "@vue/shared": "3.5.34" } }, "node_modules/@vue/devtools-api": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", - "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^7.7.7" + "@vue/devtools-kit": "^7.7.9" } }, "node_modules/@vue/devtools-kit": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", - "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^7.7.7", + "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", @@ -1937,9 +1874,9 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", - "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", "dev": true, "license": "MIT", "dependencies": { @@ -1947,53 +1884,53 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz", - "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.34.tgz", + "integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.17" + "@vue/shared": "3.5.34" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz", - "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.34.tgz", + "integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/reactivity": "3.5.34", + "@vue/shared": "3.5.34" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz", - "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz", + "integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.17", - "@vue/runtime-core": "3.5.17", - "@vue/shared": "3.5.17", - "csstype": "^3.1.3" + "@vue/reactivity": "3.5.34", + "@vue/runtime-core": "3.5.34", + "@vue/shared": "3.5.34", + "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz", - "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.34.tgz", + "integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34" }, "peerDependencies": { - "vue": "3.5.17" + "vue": "3.5.34" } }, "node_modules/@vue/shared": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz", - "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.34.tgz", + "integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==", "license": "MIT" }, "node_modules/@vuepic/vue-datepicker": { @@ -2410,9 +2347,9 @@ "license": "ISC" }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -2446,10 +2383,22 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -2482,60 +2431,49 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/algoliasearch": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.33.0.tgz", - "integrity": "sha512-WdgSkmyTec5n2W2FA2/7Q7TCSajCV0X6w57u3H5GHnw0UCp/G5xb33/Jx1FX3uMtz17P3wGEzMCP82d0LJqMow==", - "license": "MIT", - "dependencies": { - "@algolia/client-abtesting": "5.33.0", - "@algolia/client-analytics": "5.33.0", - "@algolia/client-common": "5.33.0", - "@algolia/client-insights": "5.33.0", - "@algolia/client-personalization": "5.33.0", - "@algolia/client-query-suggestions": "5.33.0", - "@algolia/client-search": "5.33.0", - "@algolia/ingestion": "1.33.0", - "@algolia/monitoring": "1.33.0", - "@algolia/recommend": "5.33.0", - "@algolia/requester-browser-xhr": "5.33.0", - "@algolia/requester-fetch": "5.33.0", - "@algolia/requester-node-http": "5.33.0" + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.52.1.tgz", + "integrity": "sha512-fHA8+kXTbjagw3jkLiaS7KKrH8qe2DyOsiUhGlN4cdT77PEsfqXZl7ewDk1hsg+pJnPlnE50XtLxjR91iJOpmg==", + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.18.1", + "@algolia/client-abtesting": "5.52.1", + "@algolia/client-analytics": "5.52.1", + "@algolia/client-common": "5.52.1", + "@algolia/client-insights": "5.52.1", + "@algolia/client-personalization": "5.52.1", + "@algolia/client-query-suggestions": "5.52.1", + "@algolia/client-search": "5.52.1", + "@algolia/ingestion": "1.52.1", + "@algolia/monitoring": "1.52.1", + "@algolia/recommend": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.26.0.tgz", - "integrity": "sha512-Rv2x3GXleQ3ygwhkhJubhhYGsICmShLAiqtUuJTUkr9uOCOXyF2E71LVT4XDnVffbknv8XgScP4U0Oxtgm+hIw==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.29.1.tgz", + "integrity": "sha512-6ck2YFudF2Pje7szQoPBiRFTGfd+1I+0I/WfLPGn0bj1kvrFoOQmNyedNiDxTk3/r4IfSLDYk+RA4G7u8H6+yA==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -2544,10 +2482,27 @@ "algoliasearch": ">= 3.1 < 6" } }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2557,6 +2512,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2587,6 +2543,18 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -2607,9 +2575,9 @@ "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", "dev": true, "funding": [ { @@ -2627,10 +2595,9 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, @@ -2645,14 +2612,15 @@ } }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/axios-retry": { @@ -2675,8 +2643,22 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.31", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz", + "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -2700,9 +2682,9 @@ } }, "node_modules/birpc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", - "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", "dev": true, "license": "MIT", "funding": { @@ -2717,9 +2699,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -2740,9 +2722,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -2760,10 +2742,11 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -2829,9 +2812,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true, "funding": [ { @@ -2959,6 +2942,39 @@ "node": ">=6.0" } }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/click-outside-vue3": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/click-outside-vue3/-/click-outside-vue3-4.0.1.tgz", @@ -2994,31 +3010,83 @@ "node": ">=12" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" }, "engines": { "node": ">= 0.8" @@ -3036,12 +3104,13 @@ } }, "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=20" } }, "node_modules/concat-map": { @@ -3052,19 +3121,18 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.0.tgz", - "integrity": "sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.2", - "lodash": "^4.17.21", - "rxjs": "^7.8.1", - "shell-quote": "^1.8.1", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.2" + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", @@ -3078,16 +3146,16 @@ } }, "node_modules/copy-anything": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", - "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", "dev": true, "license": "MIT", "dependencies": { - "is-what": "^4.1.8" + "is-what": "^5.2.0" }, "engines": { - "node": ">=12.13" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/mesqueeb" @@ -3106,6 +3174,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3135,9 +3204,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/date-fns": { @@ -3166,10 +3235,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3264,9 +3332,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", - "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.5.tgz", + "integrity": "sha512-OrwIBKsdNSVEeubdJ1HBv/wNENRM9ytAVCv7YXt//A3vPdVMNuACRqK9mXCGCBW2ln7BT/A4X0jXHo2Gu89miA==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -3296,16 +3364,10 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, "node_modules/electron-to-chromium": { - "version": "1.5.185", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.185.tgz", - "integrity": "sha512-dYOZfUk57hSMPePoIQ1fZWl1Fkj+OshhEVuPacNKWzC1efe56OsHY3l/jCfiAgIICOU3VgOIdoq7ahg7r7n6MQ==", + "version": "1.5.360", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.360.tgz", + "integrity": "sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==", "dev": true, "license": "ISC" }, @@ -3313,6 +3375,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/emoji-regex-xs": { @@ -3333,24 +3396,24 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "version": "5.21.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz", + "integrity": "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -3359,6 +3422,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -3378,9 +3454,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT", "peer": true @@ -3413,9 +3489,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3426,32 +3502,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/escalade": { @@ -3535,9 +3610,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.2.tgz", + "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", "dev": true, "license": "MIT", "bin": { @@ -3633,9 +3708,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3684,6 +3759,13 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -3744,33 +3826,32 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause", - "peer": true - }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3829,26 +3910,26 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, "node_modules/focus-trap": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz", - "integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", "dev": true, "license": "MIT", "dependencies": { - "tabbable": "^6.2.0" + "tabbable": "^6.4.0" } }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -3865,26 +3946,10 @@ } } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3898,16 +3963,16 @@ } }, "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, @@ -3951,6 +4016,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -3992,7 +4070,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -4076,9 +4154,9 @@ "peer": true }, "node_modules/gradient-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.1.1.tgz", - "integrity": "sha512-Hu0YfNU+38EsTmnUfLXUKFMXq9yz7htGYpF4x+dlbBhUCvIvzLt0yVLT/gJRmvLKFJdqNFrz4eKkIUjIXSr7Tw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.2.0.tgz", + "integrity": "sha512-6ABGa9CR7WR/0pAJicBy5SJkiikbFM6kf/JjykwX7x+t+s8ORWVnlbi6FkHeFFb36yWsjUpHqSYrygd7ofEUqA==", "engines": { "node": ">=0.10.0" } @@ -4135,9 +4213,9 @@ "license": "MIT" }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4220,6 +4298,35 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4277,42 +4384,70 @@ "license": "ISC" }, "node_modules/instantsearch-ui-components": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/instantsearch-ui-components/-/instantsearch-ui-components-0.11.2.tgz", - "integrity": "sha512-XxwqUY6NifxSvHYfyfJRiGhqYqHXQFcFNOEjNyFptB9HOkx14yCdayKar4BZxGx353FnFD6b4Z8LdzbGud+RFA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/instantsearch-ui-components/-/instantsearch-ui-components-0.27.0.tgz", + "integrity": "sha512-N+u14ae8xtvWA3vMTjUaZqu1cof3jBbkGGUMMAq+QfA+fQSrHG1nVCXqiz72zrV+oxo8+VyxmhCb0d/M6mwgzQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.27.6" + "@swc/helpers": "0.5.18", + "markdown-to-jsx": "^7.7.15", + "zod": "^3.25.76 || ^4", + "zod-to-json-schema": "3.24.6" + } + }, + "node_modules/instantsearch-ui-components/node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" } }, "node_modules/instantsearch.js": { - "version": "4.79.1", - "resolved": "https://registry.npmjs.org/instantsearch.js/-/instantsearch.js-4.79.1.tgz", - "integrity": "sha512-wqU4d3O+LHgTk+HfJaKdrhYCQkfhZ6JXD3S/WjOf0mTNH+zM9ma6ud2QdK7ebwgSwygXLNJ8Opaln+3paoVZQw==", + "version": "4.98.0", + "resolved": "https://registry.npmjs.org/instantsearch.js/-/instantsearch.js-4.98.0.tgz", + "integrity": "sha512-QGEjT5I56TmH2zmHG0NPt9Yl60EAJ3daasErB7TKMsPgNMxH+oEwW0Fqc/WkacN+ya2SZvHxMJOmNR8+sgUycw==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1", + "@swc/helpers": "0.5.18", "@types/dom-speech-recognition": "^0.0.1", "@types/google.maps": "^3.55.12", "@types/hogan.js": "^3.0.0", "@types/qs": "^6.5.3", - "algoliasearch-helper": "3.26.0", + "algoliasearch-helper": "3.29.1", "hogan.js": "^3.0.2", "htm": "^3.0.0", - "instantsearch-ui-components": "0.11.2", + "instantsearch-ui-components": "0.27.0", "preact": "^10.10.0", - "qs": "^6.5.1 < 6.10", - "search-insights": "^2.17.2" + "qs": "^6.5.1", + "react": ">= 0.14.0", + "search-insights": "^2.17.2", + "zod": "^3.25.76 || ^4", + "zod-to-json-schema": "3.24.6" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6" } }, + "node_modules/instantsearch.js/node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/instantsearch.js/node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, "engines": { "node": ">=0.6" }, @@ -4333,12 +4468,12 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -4357,12 +4492,19 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-glob": { @@ -4418,13 +4560,13 @@ } }, "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12.13" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/mesqueeb" @@ -4434,23 +4576,9 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -4476,6 +4604,12 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -4496,14 +4630,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4614,15 +4740,61 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "node_modules/lint-staged": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", + "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.3", + "listr2": "^9.0.5", + "picomatch": "^4.0.3", + "string-argv": "^0.3.2", + "tinyexec": "^1.0.4", + "yaml": "^2.8.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", "dev": true, "license": "MIT", "peer": true, "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -4657,23 +4829,16 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/lodash.castarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", - "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", - "dev": true, + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "license": "MIT" }, "node_modules/lodash.clonedeep": { @@ -4689,13 +4854,6 @@ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", "license": "MIT" }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4703,32 +4861,134 @@ "dev": true, "license": "MIT" }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, - "license": "MIT" - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-to-jsx": { + "version": "7.7.17", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.7.17.tgz", + "integrity": "sha512-7mG/1feQ0TX5I7YyMZVDgCC/y2I3CiEhIRQIhyov9nGBP5eoVrOXXHuL5ZP8GRfxVZKRiXWJgwXkb9It+nQZfQ==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -4907,6 +5167,18 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4928,6 +5200,19 @@ "node": ">= 0.6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mini-svg-data-uri": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", @@ -4939,9 +5224,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -4951,15 +5236,6 @@ "node": "*" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/minisearch": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", @@ -4987,7 +5263,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -5002,9 +5277,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -5055,9 +5330,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", + "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", "dev": true, "license": "MIT" }, @@ -5085,16 +5360,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/nprogress": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", @@ -5154,6 +5419,22 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/oniguruma-to-es": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", @@ -5175,9 +5456,9 @@ } }, "node_modules/openchemlib": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/openchemlib/-/openchemlib-7.5.0.tgz", - "integrity": "sha512-cxEmgL1Szuw5zPDX29PyuAIkokSKPkzEIc/61oPA84GqvGyjMMRrGaF4tbFCDOT4c7ULZ/qmIWk9/ERj3wOg1w==", + "version": "9.22.1", + "resolved": "https://registry.npmjs.org/openchemlib/-/openchemlib-9.22.1.tgz", + "integrity": "sha512-2eVe8uVNipaeWXuZMVk7vSqiPwyoWe2+klSjMopTDMtpr1n8hO8Q5er3+nXxBc5wMC4XRssZyVDhC/r8uj28xA==", "license": "BSD-3-Clause" }, "node_modules/optionator": { @@ -5230,12 +5511,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5273,6 +5548,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5284,22 +5560,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -5314,12 +5574,12 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -5364,9 +5624,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "funding": [ { "type": "opencollective", @@ -5383,7 +5643,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5410,9 +5670,19 @@ } }, "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -5420,14 +5690,52 @@ "engines": { "node": "^12 || ^14 || >= 16" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.4.21" } }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", @@ -5487,9 +5795,9 @@ "license": "MIT" }, "node_modules/preact": { - "version": "10.26.9", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.9.tgz", - "integrity": "sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==", + "version": "10.29.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz", + "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -5534,10 +5842,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/punycode": { "version": "2.3.1", @@ -5550,9 +5861,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -5584,15 +5895,16 @@ ], "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { - "safe-buffer": "^5.1.0" + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/read-cache": { @@ -5616,10 +5928,22 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", "dev": true, "license": "MIT", "dependencies": { @@ -5653,24 +5977,14 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -5694,6 +6008,23 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -5729,9 +6060,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", "dev": true, "license": "MIT", "dependencies": { @@ -5745,26 +6076,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", "fsevents": "~2.3.2" } }, @@ -5801,32 +6137,10 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "peer": true - }, "node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", "peer": true, @@ -5844,46 +6158,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/search-insights": { "version": "2.17.3", "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", @@ -5897,9 +6171,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, "license": "ISC", "bin": { @@ -5909,21 +6183,11 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -5936,6 +6200,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5991,13 +6256,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -6047,6 +6312,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -6055,6 +6321,36 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/sortablejs": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", @@ -6114,33 +6410,60 @@ "node": ">=0.10.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/stringify-entities": { @@ -6162,19 +6485,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6197,17 +6508,17 @@ } }, "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { @@ -6218,58 +6529,23 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 6" } }, "node_modules/superjson": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", - "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", "dev": true, "license": "MIT", "dependencies": { - "copy-anything": "^3.0.2" + "copy-anything": "^4" }, "engines": { "node": ">=16" @@ -6304,16 +6580,16 @@ } }, "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", "dev": true, "license": "MIT" }, "node_modules/tailwindcss": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", - "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -6374,41 +6650,6 @@ "postcss": "^8.0.0" } }, - "node_modules/tailwindcss/node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, "node_modules/tailwindcss/node_modules/postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", @@ -6423,26 +6664,30 @@ } }, "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "peer": true, "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", "dev": true, "license": "BSD-2-Clause", "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -6454,9 +6699,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==", "dev": true, "license": "MIT", "peer": true, @@ -6464,7 +6709,6 @@ "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "engines": { @@ -6478,12 +6722,39 @@ "webpack": "^5.1.0" }, "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, "@swc/core": { "optional": true }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, "esbuild": { "optional": true }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, "uglify-js": { "optional": true } @@ -6537,49 +6808,30 @@ "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", "license": "MIT" }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=18" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=12" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/to-regex-range": { @@ -6631,7 +6883,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -6661,17 +6912,17 @@ } }, "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT", "peer": true }, "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "dev": true, "license": "MIT", "dependencies": { @@ -6711,9 +6962,9 @@ } }, "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "dev": true, "license": "MIT", "dependencies": { @@ -6727,9 +6978,9 @@ } }, "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6742,9 +6993,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -6819,9 +7070,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6904,29 +7155,14 @@ "picomatch": "^2.3.1" } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/vite-plugin-full-reload/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -6974,403 +7210,12 @@ } } }, - "node_modules/vitepress/node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], + "node_modules/vitepress/node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vitepress/node_modules/@types/web-bluetooth": { - "version": "0.0.21", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", - "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", - "dev": true, - "license": "MIT" + "license": "MIT" }, "node_modules/vitepress/node_modules/@vueuse/core": { "version": "12.8.2", @@ -7398,45 +7243,6 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/vitepress/node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "node_modules/vitepress/node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", @@ -7498,16 +7304,16 @@ } }, "node_modules/vue": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz", - "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", + "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.17", - "@vue/compiler-sfc": "3.5.17", - "@vue/runtime-dom": "3.5.17", - "@vue/server-renderer": "3.5.17", - "@vue/shared": "3.5.17" + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-sfc": "3.5.34", + "@vue/runtime-dom": "3.5.34", + "@vue/server-renderer": "3.5.34", + "@vue/shared": "3.5.34" }, "peerDependencies": { "typescript": "*" @@ -7544,13 +7350,14 @@ } }, "node_modules/vue-instantsearch": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/vue-instantsearch/-/vue-instantsearch-4.21.1.tgz", - "integrity": "sha512-94y1Y/xaoLwyp0Fq08/OjSDG766dmE6bHMywB8eRNvdlfzQ/co2a2/0E5GFmj6VlQk3PCHmeG4SIv+9TyyAMLw==", + "version": "4.26.4", + "resolved": "https://registry.npmjs.org/vue-instantsearch/-/vue-instantsearch-4.26.4.tgz", + "integrity": "sha512-6MF1eNOOka+WNq/XMTuVQlNBJz78gmJ9vCRbHHvqfCPpaCrOkxFfhYlKtuO/bD1CF63e4NMyI2VLyquD5g/jcw==", "license": "MIT", "dependencies": { - "instantsearch-ui-components": "0.11.2", - "instantsearch.js": "4.79.1", + "@swc/helpers": "0.5.18", + "instantsearch-ui-components": "0.27.0", + "instantsearch.js": "4.98.0", "mitt": "^2.1.0" }, "peerDependencies": { @@ -7568,6 +7375,15 @@ } } }, + "node_modules/vue-instantsearch/node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/vue-instantsearch/node_modules/mitt": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz", @@ -7783,9 +7599,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", "peer": true, @@ -7804,38 +7620,36 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.100.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", - "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", + "version": "5.107.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.0.tgz", + "integrity": "sha512-PSxeHk/dmLYZlnTU+vL1Gej6Evg5RNtl3flhxBresfznFnzxinHMzHKloHnywM/3ouQv7/AlZCswWDIkNSggUA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", - "browserslist": "^4.24.0", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.2", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.21.4", + "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", + "loader-runner": "^4.3.2", + "mime-db": "^1.54.0", "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.3.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.5.0", + "watchpack": "^2.5.1", + "webpack-sources": "^3.4.1" }, "bin": { "webpack": "bin/webpack.js" @@ -7854,9 +7668,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", + "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", "dev": true, "license": "MIT", "peer": true, @@ -7890,6 +7704,17 @@ "node": ">=4.0" } }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -7904,6 +7729,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -7926,39 +7752,88 @@ } }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/wrappy": { @@ -7989,15 +7864,19 @@ } }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "devOptional": true, "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { @@ -8029,6 +7908,31 @@ "node": ">=12" } }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8042,6 +7946,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 448442efe..f96dc2a1a 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,17 @@ "format": "prettier resources/js --write", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", - "docs:preview": "vitepress preview docs" + "docs:preview": "vitepress preview docs", + "prepare": "husky" + }, + "lint-staged": { + "*.php": [ + "vendor/bin/pint" + ], + "*.{js,vue}": [ + "eslint --fix", + "prettier --write" + ] }, "devDependencies": { "@tailwindcss/forms": "^0.5.3", @@ -20,8 +30,10 @@ "eslint": "^8.21.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-vue": "^9.3.0", + "husky": "^9.1.7", "laravel-vapor": "^0.7.1", "laravel-vite-plugin": "^1.2.0", + "lint-staged": "^16.2.7", "lodash": "^4.17.19", "postcss": "^8.4.19", "postcss-import": "^16.1.0", @@ -52,9 +64,10 @@ "medium-zoom": "^1.0.8", "mitt": "^3.0.0", "ontology-elements": "^0.2.3", - "openchemlib": "^7.4.3", + "openchemlib": "^9.22.1", "pluralize": "^8.0.0", "popper.js": "^1.16.1", + "qs": "^6.14.2", "vue-instantsearch": "^4.3.3", "vue-simple-context-menu": "^4.0.4", "vue3-clipboard": "^1.0.0", @@ -62,5 +75,13 @@ "vue3-slider": "^1.7.0", "vue3-tour": "^0.2.0", "vuedraggable": "^4.1.0" + }, + "overrides": { + "ajv": "^6.14.0", + "esbuild": "0.25.0", + "instantsearch.js": { + "qs": "6.14.2" + }, + "preact": ">=10.26.10" } } diff --git a/phpunit.xml b/phpunit.xml index 5410ece8d..b5b922da1 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,20 +7,44 @@ ./tests/Feature + + ./tests/API + - + + + + + + + - + + + + + + + + + + + + ./app + + ./app/Console + ./app/Helper.php + diff --git a/public/build/manifest.json b/public/build/manifest.json index 96555c75b..09fc2b925 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -1,479 +1,773 @@ { - "_AccessDialogue-DaBf4ai0.js": { - "file": "assets/AccessDialogue-DaBf4ai0.js", + "_AccessDialogue-D2Qj7WAD.js": { + "file": "assets/AccessDialogue-D2Qj7WAD.js", "name": "AccessDialogue", "imports": [ "resources/js/app.js", - "_ActionMessage-uxM-o-da.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", - "_ToolTip-B-BuMZla.js", - "_transition-K5DyIAHH.js" - ] - }, - "_ActionMessage-uxM-o-da.js": { - "file": "assets/ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", + "_transition-Bxfvum2Z.js", + "_ChevronDownIcon-XdP1J23i.js", + "_UsersIcon-BZPR2WVE.js" + ] + }, + "_ActionMessage-Ln0XM8D-.js": { + "file": "assets/ActionMessage-Ln0XM8D-.js", "name": "ActionMessage", "imports": [ "resources/js/app.js" ] }, - "_ActionSection-DoJrofwP.js": { - "file": "assets/ActionSection-DoJrofwP.js", + "_ActionSection-BdJiuQm5.js": { + "file": "assets/ActionSection-BdJiuQm5.js", "name": "ActionSection", "imports": [ - "_SectionTitle-rK4YId1t.js", + "_SectionTitle-BuQ5xszV.js", "resources/js/app.js" ] }, - "_AnnouncementBanner-B_CIbGMm.js": { - "file": "assets/AnnouncementBanner-B_CIbGMm.js", + "_AnnouncementBanner-B6cY8Bsi.js": { + "file": "assets/AnnouncementBanner-B6cY8Bsi.js", "name": "AnnouncementBanner", "imports": [ "resources/js/app.js", - "_XMarkIcon-BrZ2BUzq.js" + "_XMarkIcon-JlHVBqMq.js" ] }, - "_AppLayout-shqwEEI1.js": { - "file": "assets/AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js": { + "file": "assets/AppLayout-czAMl_QX.js", "name": "AppLayout", "imports": [ - "_ApplicationLogo-Byie6Ini.js", - "resources/js/app.js", - "_transition-K5DyIAHH.js", - "_ToolTip-B-BuMZla.js", - "_form-Cn9CuD1E.js", - "_use-outside-click-UcI2wRsE.js", - "_use-text-value-BpD-bIcx.js", - "_hidden-2_Kmyvd6.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "resources/js/app.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "resources/js/Pages/Study/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_vue-tags-input-BGwEuaGX.js", - "_InputError-EInikEKW.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_SelectRich-Bh8aQhO2.js" - ] - }, - "_ApplicationLogo-Byie6Ini.js": { - "file": "assets/ApplicationLogo-Byie6Ini.js", + "_Button-D2g1K4pZ.js", + "_SecondaryButton-DYbDLKHA.js", + "_transition-Bxfvum2Z.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js" + ] + }, + "_ApplicationLogo-Cg0840cQ.js": { + "file": "assets/ApplicationLogo-Cg0840cQ.js", "name": "ApplicationLogo", "imports": [ "resources/js/app.js" ] }, - "_AuthenticationCard-qrykQH--.js": { - "file": "assets/AuthenticationCard-qrykQH--.js", - "name": "AuthenticationCard", + "_ArrowDownTrayIcon-CI9T5GLj.js": { + "file": "assets/ArrowDownTrayIcon-CI9T5GLj.js", + "name": "ArrowDownTrayIcon", "imports": [ "resources/js/app.js" ] }, - "_AuthenticationCardLogo-B3Uq11MZ.js": { - "file": "assets/AuthenticationCardLogo-B3Uq11MZ.js", + "_ArrowPathIcon-BW1Qv9pG.js": { + "file": "assets/ArrowPathIcon-BW1Qv9pG.js", + "name": "ArrowPathIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js": { + "file": "assets/ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "name": "ArrowTopRightOnSquareIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_AuthenticationCardLogo-Ceomgrkq.js": { + "file": "assets/AuthenticationCardLogo-Ceomgrkq.js", "name": "AuthenticationCardLogo", "imports": [ "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js" + "_ApplicationLogo-Cg0840cQ.js" + ], + "css": [ + "assets/AuthenticationCardLogo-Ch3PkjAO.css" ] }, - "_BreadCrumbs-Db7NAUm-.js": { - "file": "assets/BreadCrumbs-Db7NAUm-.js", + "_AuthenticationCardLogo-Ch3PkjAO.css": { + "file": "assets/AuthenticationCardLogo-Ch3PkjAO.css", + "src": "_AuthenticationCardLogo-Ch3PkjAO.css" + }, + "_AuthorCard-D-sml0gS.js": { + "file": "assets/AuthorCard-D-sml0gS.js", + "name": "AuthorCard", + "imports": [ + "_Tag-OosEWMYF.js", + "resources/js/app.js", + "_TrashIcon-DthmeZcs.js", + "_PencilIcon-CxfIJoOh.js" + ] + }, + "_BreadCrumbs-D_PGNu06.js": { + "file": "assets/BreadCrumbs-D_PGNu06.js", "name": "BreadCrumbs", "imports": [ "resources/js/app.js" ] }, - "_Button-Do-BJmWY.js": { - "file": "assets/Button-Do-BJmWY.js", + "_Button-D2g1K4pZ.js": { + "file": "assets/Button-D2g1K4pZ.js", "name": "Button", "imports": [ "resources/js/app.js" ] }, - "_CalendarDaysIcon-bzmmtrVA.js": { - "file": "assets/CalendarDaysIcon-bzmmtrVA.js", - "name": "CalendarDaysIcon", + "_CheckCircleIcon-BZwKPOjy.js": { + "file": "assets/CheckCircleIcon-BZwKPOjy.js", + "name": "CheckCircleIcon", "imports": [ "resources/js/app.js" ] }, - "_Checkbox-CmyOWzuc.js": { - "file": "assets/Checkbox-CmyOWzuc.js", + "_Checkbox-BWQTzG-a.js": { + "file": "assets/Checkbox-BWQTzG-a.js", "name": "Checkbox", "imports": [ "resources/js/app.js" ] }, - "_CircleStackIcon-NWc2meE7.js": { - "file": "assets/CircleStackIcon-NWc2meE7.js", + "_ChevronDownIcon-Dv1sFCyB.js": { + "file": "assets/ChevronDownIcon-Dv1sFCyB.js", + "name": "ChevronDownIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_ChevronDownIcon-XdP1J23i.js": { + "file": "assets/ChevronDownIcon-XdP1J23i.js", + "name": "ChevronDownIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_ChevronRightIcon-DO560peH.js": { + "file": "assets/ChevronRightIcon-DO560peH.js", + "name": "ChevronRightIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_ChevronUpIcon-BvtTCREq.js": { + "file": "assets/ChevronUpIcon-BvtTCREq.js", + "name": "ChevronUpIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_ChevronUpIcon-CURFCNE8.js": { + "file": "assets/ChevronUpIcon-CURFCNE8.js", + "name": "ChevronUpIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_CircleStackIcon-CwNgBqYe.js": { + "file": "assets/CircleStackIcon-CwNgBqYe.js", "name": "CircleStackIcon", "imports": [ "resources/js/app.js" ] }, - "_Citation-Dj91Ydnp.js": { - "file": "assets/Citation-Dj91Ydnp.js", + "_Citation-m9o49lqa.js": { + "file": "assets/Citation-m9o49lqa.js", "name": "Citation", "imports": [ "resources/js/app.js" ] }, - "_CitationCard-mr9Srs1M.js": { - "file": "assets/CitationCard-mr9Srs1M.js", + "_CitationCard-CAV7zOdT.js": { + "file": "assets/CitationCard-CAV7zOdT.js", "name": "CitationCard", "imports": [ - "_Tag-DRPRU5B7.js", - "resources/js/app.js" + "resources/js/app.js", + "_TrashIcon-DthmeZcs.js", + "_PencilIcon-CxfIJoOh.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js" ] }, - "_ClipboardDocumentIcon-DCnLMHiO.js": { - "file": "assets/ClipboardDocumentIcon-DCnLMHiO.js", + "_ClipboardDocumentIcon-BvbagfVF.js": { + "file": "assets/ClipboardDocumentIcon-BvbagfVF.js", "name": "ClipboardDocumentIcon", "imports": [ "resources/js/app.js" ] }, - "_ConfirmationModal-BqBcQKy5.js": { - "file": "assets/ConfirmationModal-BqBcQKy5.js", + "_Cog6ToothIcon-CTKrIXtI.js": { + "file": "assets/Cog6ToothIcon-CTKrIXtI.js", + "name": "Cog6ToothIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_CompoundCards-C7rigkx5.js": { + "file": "assets/CompoundCards-C7rigkx5.js", + "name": "CompoundCards", + "imports": [ + "_Depictor2D-CyOFcT6I.js", + "resources/js/app.js" + ] + }, + "_ConfirmationModal-D08uc7GZ.js": { + "file": "assets/ConfirmationModal-D08uc7GZ.js", "name": "ConfirmationModal", "imports": [ - "_Modal-D-_sV66W.js", + "_Modal-CIvNuTYj.js", "resources/js/app.js" ] }, - "_DOIBadge-BBho5C2T.js": { - "file": "assets/DOIBadge-BBho5C2T.js", + "_DOIBadge-Cgkqb6zh.js": { + "file": "assets/DOIBadge-Cgkqb6zh.js", "name": "DOIBadge", "imports": [ "resources/js/app.js" ] }, - "_DangerButton-DhWljAF_.js": { - "file": "assets/DangerButton-DhWljAF_.js", + "_DangerButton-Ckd3B82Q.js": { + "file": "assets/DangerButton-Ckd3B82Q.js", "name": "DangerButton", "imports": [ "resources/js/app.js" ] }, - "_Depictor2D-BloOWuSj.js": { - "file": "assets/Depictor2D-BloOWuSj.js", + "_Depictor2D-CyOFcT6I.js": { + "file": "assets/Depictor2D-CyOFcT6I.js", "name": "Depictor2D", "imports": [ "resources/js/app.js" ] }, - "_Depictor3D-DpHRqJcL.js": { - "file": "assets/Depictor3D-DpHRqJcL.js", - "name": "Depictor3D", + "_DialogModal-ClgrMrPp.js": { + "file": "assets/DialogModal-ClgrMrPp.js", + "name": "DialogModal", "imports": [ + "_Modal-CIvNuTYj.js", "resources/js/app.js" ] }, - "_DialogModal-CmZ6e9E-.js": { - "file": "assets/DialogModal-CmZ6e9E-.js", - "name": "DialogModal", + "_DocumentTextIcon-55aZskkC.js": { + "file": "assets/DocumentTextIcon-55aZskkC.js", + "name": "DocumentTextIcon", "imports": [ - "_Modal-D-_sV66W.js", "resources/js/app.js" ] }, - "_FlashMessages-H7Dz0t6O.js": { - "file": "assets/FlashMessages-H7Dz0t6O.js", - "name": "FlashMessages", + "_EllipsisVerticalIcon-CcAY43ui.js": { + "file": "assets/EllipsisVerticalIcon-CcAY43ui.js", + "name": "EllipsisVerticalIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_EmptySearchState-D1gGvUOi.js": { + "file": "assets/EmptySearchState-D1gGvUOi.js", + "name": "EmptySearchState", + "imports": [ + "resources/js/app.js", + "_XMarkIcon-JlHVBqMq.js" + ] + }, + "_ExclamationCircleIcon-Bk-CmCjh.js": { + "file": "assets/ExclamationCircleIcon-Bk-CmCjh.js", + "name": "ExclamationCircleIcon", "imports": [ "resources/js/app.js" ] }, - "_FormSection-D_YXuO3u.js": { - "file": "assets/FormSection-D_YXuO3u.js", + "_FileDetails-CtL8Ky_a.js": { + "file": "assets/FileDetails-CtL8Ky_a.js", + "name": "FileDetails", + "imports": [ + "resources/js/app.js" + ] + }, + "_FileSystemBrowser-BCIW1ZG6.js": { + "file": "assets/FileSystemBrowser-BCIW1ZG6.js", + "name": "FileSystemBrowser", + "imports": [ + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_ConfirmationModal-D08uc7GZ.js", + "_DangerButton-Ckd3B82Q.js", + "_FileDetails-CtL8Ky_a.js", + "_ToolTip-BEFzAXXI.js", + "resources/js/app.js", + "_ChevronRightIcon-DO560peH.js", + "_AppLayout-czAMl_QX.js", + "_UsersIcon-BZPR2WVE.js", + "_Squares2X2Icon-DdnPGRv-.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_ChevronUpIcon-BvtTCREq.js", + "_ScaleIcon-1OZIOuLg.js", + "_Squares2X2Icon-DGk5hsEN.js", + "_TrashIcon-DthmeZcs.js", + "_SelectRich-BJqsR0rm.js", + "_ArrowDownTrayIcon-CI9T5GLj.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_EllipsisVerticalIcon-CcAY43ui.js", + "_InformationCircleIcon-BlXpsvv4.js" + ] + }, + "_FlashMessages-Duvc-iuP.js": { + "file": "assets/FlashMessages-Duvc-iuP.js", + "name": "FlashMessages", + "imports": [ + "resources/js/app.js", + "_CheckCircleIcon-BZwKPOjy.js" + ] + }, + "_Footer-DVMJRsC3.js": { + "file": "assets/Footer-DVMJRsC3.js", + "name": "Footer", + "imports": [ + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js" + ] + }, + "_FormSection-C1YzGodw.js": { + "file": "assets/FormSection-C1YzGodw.js", "name": "FormSection", "imports": [ - "_SectionTitle-rK4YId1t.js", + "_SectionTitle-BuQ5xszV.js", "resources/js/app.js" ] }, - "_HomeIcon-B3apTAEd.js": { - "file": "assets/HomeIcon-B3apTAEd.js", + "_HomeIcon-BCWAoLnL.js": { + "file": "assets/HomeIcon-BCWAoLnL.js", "name": "HomeIcon", "imports": [ "resources/js/app.js" ] }, - "_Icon-qHt8_t4q.js": { - "file": "assets/Icon-qHt8_t4q.js", + "_Icon-D5nDZ4G4.js": { + "file": "assets/Icon-D5nDZ4G4.js", "name": "Icon", "imports": [ "resources/js/app.js" ] }, - "_Input-B-ZTzbzW.js": { - "file": "assets/Input-B-ZTzbzW.js", + "_InformationCircleIcon-BlXpsvv4.js": { + "file": "assets/InformationCircleIcon-BlXpsvv4.js", + "name": "InformationCircleIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_Input-DWYxV1Iv.js": { + "file": "assets/Input-DWYxV1Iv.js", "name": "Input", "imports": [ "resources/js/app.js" ] }, - "_InputError-EInikEKW.js": { - "file": "assets/InputError-EInikEKW.js", + "_InputError-B8XxIu7k.js": { + "file": "assets/InputError-B8XxIu7k.js", "name": "InputError", "imports": [ "resources/js/app.js" ] }, - "_Label-DGyfHV0z.js": { - "file": "assets/Label-DGyfHV0z.js", + "_Label-ChnLJ_WN.js": { + "file": "assets/Label-ChnLJ_WN.js", "name": "Label", "imports": [ "resources/js/app.js" ] }, - "_LoadingButton-DQ530rQU.js": { - "file": "assets/LoadingButton-DQ530rQU.js", + "_LoadingButton-Blde9mqi.js": { + "file": "assets/LoadingButton-Blde9mqi.js", "name": "LoadingButton", "imports": [ "resources/js/app.js" ] }, - "_LockClosedIcon-CK-FFoS7.js": { - "file": "assets/LockClosedIcon-CK-FFoS7.js", + "_LockClosedIcon-BgJuctHv.js": { + "file": "assets/LockClosedIcon-BgJuctHv.js", + "name": "LockClosedIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_LockClosedIcon-CAD5UECp.js": { + "file": "assets/LockClosedIcon-CAD5UECp.js", "name": "LockClosedIcon", "imports": [ "resources/js/app.js" ] }, - "_MagnifyingGlassIcon-Cq7Ou0Fo.js": { - "file": "assets/MagnifyingGlassIcon-Cq7Ou0Fo.js", + "_MagnifyingGlassIcon-Dp6MxRhw.js": { + "file": "assets/MagnifyingGlassIcon-Dp6MxRhw.js", "name": "MagnifyingGlassIcon", "imports": [ - "_portal-viea1unE.js", - "resources/js/app.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js" + "resources/js/app.js" ] }, - "_ManageCitation-A3-Z6aBb.js": { - "file": "assets/ManageCitation-A3-Z6aBb.js", + "_ManageCitation-XxXeFAB5.js": { + "file": "assets/ManageCitation-XxXeFAB5.js", "name": "ManageCitation", "imports": [ - "_DialogModal-CmZ6e9E-.js", - "_SecondaryButton-BucXplhe.js", - "_InputError-EInikEKW.js", - "_LoadingButton-DQ530rQU.js", - "_DangerButton-DhWljAF_.js", + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_InputError-B8XxIu7k.js", + "_LoadingButton-Blde9mqi.js", + "_DangerButton-Ckd3B82Q.js", "resources/js/app.js", - "_SelectRich-Bh8aQhO2.js", - "_CitationCard-mr9Srs1M.js", - "_AppLayout-shqwEEI1.js", - "_vue-tags-input-BGwEuaGX.js" + "_SelectRich-BJqsR0rm.js", + "_vue-tags-input-CyZTEOCK.js", + "_AuthorCard-D-sml0gS.js", + "_RorAffiliationTypeahead-BH6zDoc8.js", + "_TrashIcon-DthmeZcs.js", + "_PencilIcon-CxfIJoOh.js", + "_CitationCard-CAV7zOdT.js" ] }, - "_Modal-D-_sV66W.js": { - "file": "assets/Modal-D-_sV66W.js", + "_Modal-CIvNuTYj.js": { + "file": "assets/Modal-CIvNuTYj.js", "name": "Modal", "imports": [ "resources/js/app.js" ] }, - "_Pagination-DUPh-k9M.js": { - "file": "assets/Pagination-DUPh-k9M.js", + "_MolecularInfoPanel-CRODCv3s.js": { + "file": "assets/MolecularInfoPanel-CRODCv3s.js", + "name": "MolecularInfoPanel", + "imports": [ + "_Depictor2D-CyOFcT6I.js", + "resources/js/app.js" + ] + }, + "_Pagination-DTxo9TqJ.js": { + "file": "assets/Pagination-DTxo9TqJ.js", "name": "Pagination", "imports": [ "resources/js/app.js" ] }, - "_ProjectCard-C7JLsgIL.js": { - "file": "assets/ProjectCard-C7JLsgIL.js", + "_PencilIcon-CxfIJoOh.js": { + "file": "assets/PencilIcon-CxfIJoOh.js", + "name": "PencilIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_ProjectCard-BV_YGrVn.js": { + "file": "assets/ProjectCard-BV_YGrVn.js", "name": "ProjectCard", "imports": [ "resources/js/app.js", - "_Tag-DRPRU5B7.js", - "_ScaleIcon-CYgZpCW4.js", - "_ToolTip-B-BuMZla.js" + "_Tag-OosEWMYF.js", + "_SeededCoverBackground-CkmszP3E.js", + "_ScaleIcon-BGti8Jud.js", + "_ArrowDownTrayIcon-CI9T5GLj.js", + "_UsersIcon-BZPR2WVE.js", + "_EllipsisVerticalIcon-CcAY43ui.js" + ] + }, + "_PublicDatasetBody-D9fy91sT.js": { + "file": "assets/PublicDatasetBody-D9fy91sT.js", + "name": "PublicDatasetBody", + "imports": [ + "_SpectraViewer-Cl8c4T6A.js", + "_DOIBadge-Cgkqb6zh.js", + "_MolecularInfoPanel-CRODCv3s.js", + "resources/js/app.js", + "_UsersIcon-BZPR2WVE.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ShareIcon-Ds-VRaVF.js" + ] + }, + "_Publish-CArzv3FY.js": { + "file": "assets/Publish-CArzv3FY.js", + "name": "Publish", + "imports": [ + "resources/js/app.js" + ] + }, + "_QueueListIcon-pHLuzOz3.js": { + "file": "assets/QueueListIcon-pHLuzOz3.js", + "name": "QueueListIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_RorAffiliationTypeahead-BH6zDoc8.js": { + "file": "assets/RorAffiliationTypeahead-BH6zDoc8.js", + "name": "RorAffiliationTypeahead", + "imports": [ + "_Input-DWYxV1Iv.js", + "resources/js/app.js" + ] + }, + "_ScaleIcon-1OZIOuLg.js": { + "file": "assets/ScaleIcon-1OZIOuLg.js", + "name": "ScaleIcon", + "imports": [ + "resources/js/app.js" ] }, - "_ScaleIcon-CYgZpCW4.js": { - "file": "assets/ScaleIcon-CYgZpCW4.js", + "_ScaleIcon-BGti8Jud.js": { + "file": "assets/ScaleIcon-BGti8Jud.js", "name": "ScaleIcon", "imports": [ "resources/js/app.js" ] }, - "_SecondaryButton-BucXplhe.js": { - "file": "assets/SecondaryButton-BucXplhe.js", + "_SecondaryButton-DYbDLKHA.js": { + "file": "assets/SecondaryButton-DYbDLKHA.js", "name": "SecondaryButton", "imports": [ "resources/js/app.js" ] }, - "_SectionBorder-Dz4Jb6r4.js": { - "file": "assets/SectionBorder-Dz4Jb6r4.js", + "_SectionBorder-9kJNWtL7.js": { + "file": "assets/SectionBorder-9kJNWtL7.js", "name": "SectionBorder", "imports": [ "resources/js/app.js" ] }, - "_SectionTitle-rK4YId1t.js": { - "file": "assets/SectionTitle-rK4YId1t.js", + "_SectionTitle-BuQ5xszV.js": { + "file": "assets/SectionTitle-BuQ5xszV.js", "name": "SectionTitle", "imports": [ "resources/js/app.js" ] }, - "_SelectOrcidId-D9IuGi3f.js": { - "file": "assets/SelectOrcidId-D9IuGi3f.js", + "_SeededCoverBackground-CkmszP3E.js": { + "file": "assets/SeededCoverBackground-CkmszP3E.js", + "name": "SeededCoverBackground", + "imports": [ + "resources/js/app.js" + ] + }, + "_SelectOrcidId-Zpq-5XGg.js": { + "file": "assets/SelectOrcidId-Zpq-5XGg.js", "name": "SelectOrcidId", "imports": [ - "_SecondaryButton-BucXplhe.js", - "_DialogModal-CmZ6e9E-.js", - "_LoadingButton-DQ530rQU.js", + "_SecondaryButton-DYbDLKHA.js", + "_DialogModal-ClgrMrPp.js", + "_LoadingButton-Blde9mqi.js", "resources/js/app.js" ] }, - "_SelectRich-Bh8aQhO2.js": { - "file": "assets/SelectRich-Bh8aQhO2.js", + "_SelectRich-BJqsR0rm.js": { + "file": "assets/SelectRich-BJqsR0rm.js", "name": "SelectRich", "imports": [ "resources/js/app.js", - "_hidden-2_Kmyvd6.js", - "_use-outside-click-UcI2wRsE.js", - "_micro-task-CxIZtCgj.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js" + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js" ] }, - "_ShareIcon-Bhy_NH0G.js": { - "file": "assets/ShareIcon-Bhy_NH0G.js", + "_ShareIcon-Ds-VRaVF.js": { + "file": "assets/ShareIcon-Ds-VRaVF.js", "name": "ShareIcon", "imports": [ "resources/js/app.js" ] }, - "_ShowProjectDates-DEyhF2bt.js": { - "file": "assets/ShowProjectDates-DEyhF2bt.js", + "_ShowProjectDates-coZINevl.js": { + "file": "assets/ShowProjectDates-coZINevl.js", "name": "ShowProjectDates", + "imports": [ + "resources/js/app.js" + ] + }, + "_SpectraEditor-DGo8KwaT.js": { + "file": "assets/SpectraEditor-DGo8KwaT.js", + "name": "SpectraEditor", "imports": [ "resources/js/app.js", - "_CalendarDaysIcon-bzmmtrVA.js" + "_XMarkIcon-JlHVBqMq.js", + "_transition-Bxfvum2Z.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_ArrowPathIcon-BW1Qv9pG.js" ] }, - "_SpectraViewer-DAiohBoS.js": { - "file": "assets/SpectraViewer-DAiohBoS.js", + "_SpectraViewer-Cl8c4T6A.js": { + "file": "assets/SpectraViewer-Cl8c4T6A.js", "name": "SpectraViewer", "imports": [ "resources/js/app.js" ] }, - "_StarIcon-BYJ8kyod.js": { - "file": "assets/StarIcon-BYJ8kyod.js", + "_Squares2X2Icon-DGk5hsEN.js": { + "file": "assets/Squares2X2Icon-DGk5hsEN.js", + "name": "Squares2X2Icon", + "imports": [ + "resources/js/app.js" + ] + }, + "_Squares2X2Icon-DdnPGRv-.js": { + "file": "assets/Squares2X2Icon-DdnPGRv-.js", + "name": "Squares2X2Icon", + "imports": [ + "resources/js/app.js" + ] + }, + "_StarIcon-DrloavXH.js": { + "file": "assets/StarIcon-DrloavXH.js", "name": "StarIcon", "imports": [ "resources/js/app.js" ] }, - "_StructureSearch-BAHFDTET.js": { - "file": "assets/StructureSearch-BAHFDTET.js", + "_StructureSearch-CTChAZlj.js": { + "file": "assets/StructureSearch-CTChAZlj.js", "name": "StructureSearch", "imports": [ "resources/js/app.js", - "_ToolTip-B-BuMZla.js", - "_transition-K5DyIAHH.js" + "_structureEditor-B2P_9D5s.js", + "_transition-Bxfvum2Z.js" + ], + "dynamicImports": [ + "resources/js/app.js" ] }, - "_StudyCard-CZirq0ik.js": { - "file": "assets/StudyCard-CZirq0ik.js", + "_StudyCard-CbFXlVaX.js": { + "file": "assets/StudyCard-CbFXlVaX.js", "name": "StudyCard", "imports": [ "resources/js/app.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js" + "_Depictor2D-CyOFcT6I.js", + "_LockClosedIcon-BgJuctHv.js" ] }, - "_StudyCardPublic-03l-9aGV.js": { - "file": "assets/StudyCardPublic-03l-9aGV.js", + "_StudyCardPublic-DMMRkFmY.js": { + "file": "assets/StudyCardPublic-DMMRkFmY.js", "name": "StudyCardPublic", "imports": [ "resources/js/app.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js" + "_Depictor2D-CyOFcT6I.js", + "_LockClosedIcon-CAD5UECp.js" ] }, - "_SuccessButton-CQHcc1CH.js": { - "file": "assets/SuccessButton-CQHcc1CH.js", + "_SuccessButton-BKHqQWO5.js": { + "file": "assets/SuccessButton-BKHqQWO5.js", "name": "SuccessButton", "imports": [ "resources/js/app.js" ] }, - "_Tag-DRPRU5B7.js": { - "file": "assets/Tag-DRPRU5B7.js", + "_Tag-OosEWMYF.js": { + "file": "assets/Tag-OosEWMYF.js", "name": "Tag", "imports": [ "resources/js/app.js" ] }, - "_ToggleButton-C0L2QH2i.js": { - "file": "assets/ToggleButton-C0L2QH2i.js", + "_ToggleButton-BhhLdTGV.js": { + "file": "assets/ToggleButton-BhhLdTGV.js", "name": "ToggleButton", "imports": [ "resources/js/app.js", - "_switch-DyCbjYu6.js" + "_switch-BsWHPT9U.js" ] }, - "_ToolTip-B-BuMZla.js": { - "file": "assets/ToolTip-B-BuMZla.js", + "_ToolTip-BEFzAXXI.js": { + "file": "assets/ToolTip-BEFzAXXI.js", "name": "ToolTip", + "imports": [ + "resources/js/app.js" + ] + }, + "_TrashIcon-DthmeZcs.js": { + "file": "assets/TrashIcon-DthmeZcs.js", + "name": "TrashIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_TrashIcon-hldFMpdc.js": { + "file": "assets/TrashIcon-hldFMpdc.js", + "name": "TrashIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_UserGroupIcon-v_dx-FoW.js": { + "file": "assets/UserGroupIcon-v_dx-FoW.js", + "name": "UserGroupIcon", + "imports": [ + "resources/js/app.js" + ] + }, + "_UsersIcon-BZPR2WVE.js": { + "file": "assets/UsersIcon-BZPR2WVE.js", + "name": "UsersIcon", + "imports": [ + "resources/js/app.js", + "_use-outside-click-BIOUF4IU.js", + "_use-text-value-ZZ3p0KKx.js" + ] + }, + "_Validation-racvfLq0.js": { + "file": "assets/Validation-racvfLq0.js", + "name": "Validation", "imports": [ "resources/js/app.js", - "_use-outside-click-UcI2wRsE.js", - "_use-text-value-BpD-bIcx.js" + "_ChevronUpIcon-CURFCNE8.js" ] }, - "_ValidationErrors-Cs3aqeYy.js": { - "file": "assets/ValidationErrors-Cs3aqeYy.js", + "_ValidationErrors-DHtFos5n.js": { + "file": "assets/ValidationErrors-DHtFos5n.js", "name": "ValidationErrors", "imports": [ "resources/js/app.js" ] }, - "_XMarkIcon-BrZ2BUzq.js": { - "file": "assets/XMarkIcon-BrZ2BUzq.js", + "_XMarkIcon-JlHVBqMq.js": { + "file": "assets/XMarkIcon-JlHVBqMq.js", "name": "XMarkIcon", "imports": [ "resources/js/app.js" ] }, - "_description-D4q-ya34.js": { - "file": "assets/description-D4q-ya34.js", + "_active-element-history-ElNFAgva.js": { + "file": "assets/active-element-history-ElNFAgva.js", + "name": "active-element-history" + }, + "_description-R20iYW7s.js": { + "file": "assets/description-R20iYW7s.js", "name": "description", "imports": [ "resources/js/app.js" ] }, - "_form-Cn9CuD1E.js": { - "file": "assets/form-Cn9CuD1E.js", + "_form-B25fLBcn.js": { + "file": "assets/form-B25fLBcn.js", "name": "form", "imports": [ "resources/js/app.js" ] }, - "_hidden-2_Kmyvd6.js": { - "file": "assets/hidden-2_Kmyvd6.js", + "_hidden-CMJ14BdS.js": { + "file": "assets/hidden-CMJ14BdS.js", "name": "hidden", "imports": [ "resources/js/app.js" ] }, - "_index-CwURvEqp.js": { - "file": "assets/index-CwURvEqp.js", + "_index-6dcjgqnm.js": { + "file": "assets/index-6dcjgqnm.js", + "name": "index", + "imports": [ + "resources/js/app.js" + ] + }, + "_index-CIK-HhQI.js": { + "file": "assets/index-CIK-HhQI.js", "name": "index", "imports": [ "resources/js/app.js" @@ -483,8 +777,8 @@ "file": "assets/main-CITaNMI7.css", "src": "_main-CITaNMI7.css" }, - "_main-DPI-_6Az.js": { - "file": "assets/main-DPI-_6Az.js", + "_main-Did928Rw.js": { + "file": "assets/main-Did928Rw.js", "name": "main", "imports": [ "resources/js/app.js" @@ -493,2068 +787,2499 @@ "assets/main-CITaNMI7.css" ] }, - "_micro-task-CxIZtCgj.js": { - "file": "assets/micro-task-CxIZtCgj.js", - "name": "micro-task" - }, - "_pickBy-lvn3s5H6.js": { - "file": "assets/pickBy-lvn3s5H6.js", + "_pickBy-Cq4gBaYP.js": { + "file": "assets/pickBy-Cq4gBaYP.js", "name": "pickBy", "imports": [ "resources/js/app.js" ] }, - "_portal-viea1unE.js": { - "file": "assets/portal-viea1unE.js", + "_popover-B4upHQWP.js": { + "file": "assets/popover-B4upHQWP.js", + "name": "popover", + "imports": [ + "_portal-FA1zWb63.js", + "resources/js/app.js", + "_use-outside-click-BIOUF4IU.js", + "_hidden-CMJ14BdS.js" + ] + }, + "_portal-FA1zWb63.js": { + "file": "assets/portal-FA1zWb63.js", "name": "portal", "imports": [ - "_use-outside-click-UcI2wRsE.js", + "_use-outside-click-BIOUF4IU.js", "resources/js/app.js", - "_hidden-2_Kmyvd6.js" + "_hidden-CMJ14BdS.js" ] }, - "_style-2QlRVEvL.js": { - "file": "assets/style-2QlRVEvL.js", - "name": "style", + "_structureEditor-B2P_9D5s.js": { + "file": "assets/structureEditor-B2P_9D5s.js", + "name": "structureEditor", "imports": [ "resources/js/app.js" - ], - "css": [ - "assets/style-D0KUwiFO.css" ] }, "_style-D0KUwiFO.css": { "file": "assets/style-D0KUwiFO.css", "src": "_style-D0KUwiFO.css" }, - "_switch-DyCbjYu6.js": { - "file": "assets/switch-DyCbjYu6.js", + "_style-Ni5A2mqp.js": { + "file": "assets/style-Ni5A2mqp.js", + "name": "style", + "imports": [ + "resources/js/app.js" + ], + "css": [ + "assets/style-D0KUwiFO.css" + ] + }, + "_switch-BsWHPT9U.js": { + "file": "assets/switch-BsWHPT9U.js", "name": "switch", "imports": [ - "_form-Cn9CuD1E.js", + "_form-B25fLBcn.js", "resources/js/app.js", - "_hidden-2_Kmyvd6.js", - "_description-D4q-ya34.js" + "_hidden-CMJ14BdS.js", + "_description-R20iYW7s.js" ] }, - "_transition-K5DyIAHH.js": { - "file": "assets/transition-K5DyIAHH.js", + "_transition-Bxfvum2Z.js": { + "file": "assets/transition-Bxfvum2Z.js", "name": "transition", "imports": [ - "_portal-viea1unE.js", - "_hidden-2_Kmyvd6.js", + "_portal-FA1zWb63.js", + "_hidden-CMJ14BdS.js", + "_active-element-history-ElNFAgva.js", "resources/js/app.js", - "_use-outside-click-UcI2wRsE.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js" + "_use-outside-click-BIOUF4IU.js", + "_description-R20iYW7s.js" ] }, - "_use-outside-click-UcI2wRsE.js": { - "file": "assets/use-outside-click-UcI2wRsE.js", + "_use-outside-click-BIOUF4IU.js": { + "file": "assets/use-outside-click-BIOUF4IU.js", "name": "use-outside-click", "imports": [ "resources/js/app.js" ] }, - "_use-text-value-BpD-bIcx.js": { - "file": "assets/use-text-value-BpD-bIcx.js", + "_use-text-value-ZZ3p0KKx.js": { + "file": "assets/use-text-value-ZZ3p0KKx.js", "name": "use-text-value", "imports": [ - "resources/js/app.js" + "resources/js/app.js", + "_use-outside-click-BIOUF4IU.js" ] }, - "_vue-tags-input-BGwEuaGX.js": { - "file": "assets/vue-tags-input-BGwEuaGX.js", + "_vue-tags-input-CyZTEOCK.js": { + "file": "assets/vue-tags-input-CyZTEOCK.js", "name": "vue-tags-input", "imports": [ "resources/js/app.js" ] }, + "_vue3-slider.common-DgsdEvX8.js": { + "file": "assets/vue3-slider.common-DgsdEvX8.js", + "name": "vue3-slider.common", + "imports": [ + "resources/js/app.js", + "_vue-tags-input-CyZTEOCK.js" + ] + }, + "node_modules/openchemlib/dist/resources.json": { + "file": "assets/resources-D5ymtJwZ.json", + "src": "node_modules/openchemlib/dist/resources.json" + }, + "resources/css/app.css": { + "file": "assets/app-Cb11sZv5.css", + "src": "resources/css/app.css", + "isEntry": true + }, "resources/js/Pages/API/Index.vue": { - "file": "assets/Index-wDDwTVt7.js", + "file": "assets/Index-BPuDLdAn.js", "name": "Index", "src": "resources/js/Pages/API/Index.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/API/Partials/ApiTokenManager.vue", - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_ActionMessage-uxM-o-da.js", - "_ActionSection-DoJrofwP.js", - "_SectionTitle-rK4YId1t.js", - "_Button-Do-BJmWY.js", - "_ConfirmationModal-BqBcQKy5.js", - "_Modal-D-_sV66W.js", - "_DangerButton-DhWljAF_.js", - "_DialogModal-CmZ6e9E-.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_Checkbox-CmyOWzuc.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", - "_SecondaryButton-BucXplhe.js", - "_SectionBorder-Dz4Jb6r4.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_ActionMessage-Ln0XM8D-.js", + "_ActionSection-BdJiuQm5.js", + "_SectionTitle-BuQ5xszV.js", + "_Button-D2g1K4pZ.js", + "_ConfirmationModal-D08uc7GZ.js", + "_Modal-CIvNuTYj.js", + "_DangerButton-Ckd3B82Q.js", + "_DialogModal-ClgrMrPp.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_Checkbox-BWQTzG-a.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", + "_SecondaryButton-DYbDLKHA.js", + "_SectionBorder-9kJNWtL7.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js" + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/API/Partials/ApiTokenManager.vue": { - "file": "assets/ApiTokenManager-CrMpqUch.js", + "file": "assets/ApiTokenManager-zz73oieR.js", "name": "ApiTokenManager", "src": "resources/js/Pages/API/Partials/ApiTokenManager.vue", "isDynamicEntry": true, "imports": [ - "_ActionMessage-uxM-o-da.js", - "_ActionSection-DoJrofwP.js", - "_Button-Do-BJmWY.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_DialogModal-CmZ6e9E-.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_Checkbox-CmyOWzuc.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", - "_SecondaryButton-BucXplhe.js", - "_SectionBorder-Dz4Jb6r4.js", + "_ActionMessage-Ln0XM8D-.js", + "_ActionSection-BdJiuQm5.js", + "_Button-D2g1K4pZ.js", + "_ConfirmationModal-D08uc7GZ.js", + "_DangerButton-Ckd3B82Q.js", + "_DialogModal-ClgrMrPp.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_Checkbox-BWQTzG-a.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", + "_SecondaryButton-DYbDLKHA.js", + "_SectionBorder-9kJNWtL7.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/About.vue": { - "file": "assets/About-DsSjYaOL.js", + "file": "assets/About-BFocVUVt.js", "name": "About", "src": "resources/js/Pages/About.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_FlashMessages-H7Dz0t6O.js", - "_MagnifyingGlassIcon-Cq7Ou0Fo.js", - "_XMarkIcon-BrZ2BUzq.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js" + "_ApplicationLogo-Cg0840cQ.js", + "_FlashMessages-Duvc-iuP.js", + "_Footer-DVMJRsC3.js", + "_popover-B4upHQWP.js", + "_XMarkIcon-JlHVBqMq.js", + "_MagnifyingGlassIcon-Dp6MxRhw.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_portal-FA1zWb63.js", + "_use-outside-click-BIOUF4IU.js", + "_hidden-CMJ14BdS.js" + ], + "css": [ + "assets/About-TJe_R_Qe.css", + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Announcement/Index.vue": { - "file": "assets/Index-BZzqK1VE.js", + "file": "assets/Index-BRXF06UH.js", "name": "Index", "src": "resources/js/Pages/Announcement/Index.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_BreadCrumbs-Db7NAUm-.js", - "_Icon-qHt8_t4q.js", + "_AppLayout-czAMl_QX.js", + "_BreadCrumbs-D_PGNu06.js", + "_Icon-D5nDZ4G4.js", "resources/js/Pages/Announcement/Partials/Create.vue", "resources/js/Pages/Announcement/Partials/Edit.vue", - "_DangerButton-DhWljAF_.js", - "_SecondaryButton-BucXplhe.js", - "_DialogModal-CmZ6e9E-.js", - "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_DangerButton-Ckd3B82Q.js", + "_SecondaryButton-DYbDLKHA.js", + "_DialogModal-ClgrMrPp.js", + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_Modal-D-_sV66W.js", - "_ToggleButton-C0L2QH2i.js", - "_main-DPI-_6Az.js" + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ToggleButton-BhhLdTGV.js", + "_main-Did928Rw.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Announcement/Partials/Create.vue": { - "file": "assets/Create-Dxec--Kk.js", + "file": "assets/Create-BNLGz6Zu.js", "name": "Create", "src": "resources/js/Pages/Announcement/Partials/Create.vue", "isDynamicEntry": true, "imports": [ - "_DialogModal-CmZ6e9E-.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_ToggleButton-C0L2QH2i.js", - "_main-DPI-_6Az.js", + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_ToggleButton-BhhLdTGV.js", + "_main-Did928Rw.js", "resources/js/app.js", - "_Modal-D-_sV66W.js", - "_switch-DyCbjYu6.js", - "_form-Cn9CuD1E.js", - "_hidden-2_Kmyvd6.js", - "_description-D4q-ya34.js" + "_Modal-CIvNuTYj.js", + "_switch-BsWHPT9U.js", + "_form-B25fLBcn.js", + "_hidden-CMJ14BdS.js", + "_description-R20iYW7s.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Announcement/Partials/Edit.vue": { - "file": "assets/Edit-Dr7xby21.js", + "file": "assets/Edit-Dc8l40Hj.js", "name": "Edit", "src": "resources/js/Pages/Announcement/Partials/Edit.vue", "isDynamicEntry": true, "imports": [ - "_DialogModal-CmZ6e9E-.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_ToggleButton-C0L2QH2i.js", - "_main-DPI-_6Az.js", + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_ToggleButton-BhhLdTGV.js", + "_main-Did928Rw.js", "resources/js/app.js", - "_Modal-D-_sV66W.js", - "_switch-DyCbjYu6.js", - "_form-Cn9CuD1E.js", - "_hidden-2_Kmyvd6.js", - "_description-D4q-ya34.js" + "_Modal-CIvNuTYj.js", + "_switch-BsWHPT9U.js", + "_form-B25fLBcn.js", + "_hidden-CMJ14BdS.js", + "_description-R20iYW7s.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/ConfirmPassword.vue": { - "file": "assets/ConfirmPassword-C3_l58cZ.js", + "file": "assets/ConfirmPassword-Blb6dcg-.js", "name": "ConfirmPassword", "src": "resources/js/Pages/Auth/ConfirmPassword.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ValidationErrors-Cs3aqeYy.js", - "_ApplicationLogo-Byie6Ini.js" + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ValidationErrors-DHtFos5n.js", + "_ApplicationLogo-Cg0840cQ.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/ForgotPassword.vue": { - "file": "assets/ForgotPassword-BesprtPD.js", + "file": "assets/ForgotPassword-Cm4e_DnG.js", "name": "ForgotPassword", "src": "resources/js/Pages/Auth/ForgotPassword.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ValidationErrors-Cs3aqeYy.js", - "_ApplicationLogo-Byie6Ini.js" + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ValidationErrors-DHtFos5n.js", + "_ApplicationLogo-Cg0840cQ.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/Login.vue": { - "file": "assets/Login-C3yEUbjr.js", + "file": "assets/Login-DYm7ynfM.js", "name": "Login", "src": "resources/js/Pages/Auth/Login.vue", "isDynamicEntry": true, "imports": [ - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_Checkbox-CmyOWzuc.js", - "_Label-DGyfHV0z.js", - "_ValidationErrors-Cs3aqeYy.js", + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_Checkbox-BWQTzG-a.js", + "_Label-ChnLJ_WN.js", + "_ValidationErrors-DHtFos5n.js", "resources/js/app.js", - "_AnnouncementBanner-B_CIbGMm.js", - "_ApplicationLogo-Byie6Ini.js", - "_XMarkIcon-BrZ2BUzq.js" + "_AnnouncementBanner-B6cY8Bsi.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/Register.vue": { - "file": "assets/Register-DtrO_O1N.js", + "file": "assets/Register-CApyqPfU.js", "name": "Register", "src": "resources/js/Pages/Auth/Register.vue", "isDynamicEntry": true, "imports": [ - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_Checkbox-CmyOWzuc.js", - "_Label-DGyfHV0z.js", - "_ValidationErrors-Cs3aqeYy.js", + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_Checkbox-BWQTzG-a.js", + "_Label-ChnLJ_WN.js", + "_ValidationErrors-DHtFos5n.js", "resources/js/app.js", - "_AnnouncementBanner-B_CIbGMm.js", - "_InputError-EInikEKW.js", - "_SelectOrcidId-D9IuGi3f.js", - "_ApplicationLogo-Byie6Ini.js", - "_XMarkIcon-BrZ2BUzq.js", - "_SecondaryButton-BucXplhe.js", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_LoadingButton-DQ530rQU.js" + "_AnnouncementBanner-B6cY8Bsi.js", + "_InputError-B8XxIu7k.js", + "_SelectOrcidId-Zpq-5XGg.js", + "_RorAffiliationTypeahead-BH6zDoc8.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_SecondaryButton-DYbDLKHA.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_LoadingButton-Blde9mqi.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/ResetPassword.vue": { - "file": "assets/ResetPassword--TjZNsBn.js", + "file": "assets/ResetPassword-B0lqN-7r.js", "name": "ResetPassword", "src": "resources/js/Pages/Auth/ResetPassword.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ValidationErrors-Cs3aqeYy.js", - "_ApplicationLogo-Byie6Ini.js" + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ValidationErrors-DHtFos5n.js", + "_ApplicationLogo-Cg0840cQ.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/SetPassword.vue": { - "file": "assets/SetPassword-Dw4CoBJs.js", + "file": "assets/SetPassword-8YM8wrFe.js", "name": "SetPassword", "src": "resources/js/Pages/Auth/SetPassword.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ValidationErrors-Cs3aqeYy.js", - "_ApplicationLogo-Byie6Ini.js" + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ValidationErrors-DHtFos5n.js", + "_ApplicationLogo-Cg0840cQ.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/TwoFactorChallenge.vue": { - "file": "assets/TwoFactorChallenge-ua3B007q.js", + "file": "assets/TwoFactorChallenge-Dbue1ycD.js", "name": "TwoFactorChallenge", "src": "resources/js/Pages/Auth/TwoFactorChallenge.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ValidationErrors-Cs3aqeYy.js", - "_ApplicationLogo-Byie6Ini.js" + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ValidationErrors-DHtFos5n.js", + "_ApplicationLogo-Cg0840cQ.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Auth/VerifyEmail.vue": { - "file": "assets/VerifyEmail-Dy2xDSk0.js", + "file": "assets/VerifyEmail-B8YTFZBh.js", "name": "VerifyEmail", "src": "resources/js/Pages/Auth/VerifyEmail.vue", "isDynamicEntry": true, "imports": [ - "_AuthenticationCard-qrykQH--.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_Button-Do-BJmWY.js", + "_AuthenticationCardLogo-Ceomgrkq.js", + "_Button-D2g1K4pZ.js", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js" + "_ApplicationLogo-Cg0840cQ.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Index.vue": { - "file": "assets/Index-DNqCPZuu.js", + "file": "assets/Index-BXLVKgEt.js", "name": "Index", "src": "resources/js/Pages/Console/Index.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_Icon-qHt8_t4q.js", - "_ToolTip-B-BuMZla.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_Icon-D5nDZ4G4.js", + "_EllipsisVerticalIcon-CcAY43ui.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Spectra/Index.vue": { - "file": "assets/Index-xVZ32gep.js", + "file": "assets/Index-PzBhrFic.js", "name": "Index", "src": "resources/js/Pages/Console/Spectra/Index.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_BreadCrumbs-Db7NAUm-.js", - "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_BreadCrumbs-D_PGNu06.js", + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Spectra/Snapshot.vue": { - "file": "assets/Snapshot-PNdJOEoO.js", + "file": "assets/Snapshot-CTrRaIm9.js", "name": "Snapshot", "src": "resources/js/Pages/Console/Spectra/Snapshot.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_BreadCrumbs-Db7NAUm-.js", - "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_BreadCrumbs-D_PGNu06.js", + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Users/Create.vue": { - "file": "assets/Create-BnHnkykD.js", + "file": "assets/Create-Du8js_AX.js", "name": "Create", "src": "resources/js/Pages/Console/Users/Create.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_BreadCrumbs-Db7NAUm-.js", + "_AppLayout-czAMl_QX.js", + "_BreadCrumbs-D_PGNu06.js", "resources/js/Pages/Console/Users/Partials/UserProfile.vue", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_FormSection-D_YXuO3u.js", - "_SectionTitle-rK4YId1t.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ActionMessage-uxM-o-da.js", - "_SelectOrcidId-D9IuGi3f.js", - "_LoadingButton-DQ530rQU.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_FormSection-C1YzGodw.js", + "_SectionTitle-BuQ5xszV.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ActionMessage-Ln0XM8D-.js", + "_SelectOrcidId-Zpq-5XGg.js", + "_LoadingButton-Blde9mqi.js", + "_RorAffiliationTypeahead-BH6zDoc8.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Users/Edit.vue": { - "file": "assets/Edit-C6WGund9.js", + "file": "assets/Edit-B0xyJRFu.js", "name": "Edit", "src": "resources/js/Pages/Console/Users/Edit.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_BreadCrumbs-Db7NAUm-.js", + "_AppLayout-czAMl_QX.js", + "_BreadCrumbs-D_PGNu06.js", "resources/js/Pages/Console/Users/Partials/UserProfile.vue", "resources/js/Pages/Console/Users/Partials/UserPassword.vue", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_FormSection-D_YXuO3u.js", - "_SectionTitle-rK4YId1t.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ActionMessage-uxM-o-da.js", - "_SelectOrcidId-D9IuGi3f.js", - "_LoadingButton-DQ530rQU.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_FormSection-C1YzGodw.js", + "_SectionTitle-BuQ5xszV.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ActionMessage-Ln0XM8D-.js", + "_SelectOrcidId-Zpq-5XGg.js", + "_LoadingButton-Blde9mqi.js", + "_RorAffiliationTypeahead-BH6zDoc8.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Users/Index.vue": { - "file": "assets/Index-Chvea0vj.js", + "file": "assets/Index-bmyycHH9.js", "name": "Index", "src": "resources/js/Pages/Console/Users/Index.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_pickBy-lvn3s5H6.js", - "resources/js/app.js", - "_DialogModal-CmZ6e9E-.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_BreadCrumbs-Db7NAUm-.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_pickBy-Cq4gBaYP.js", + "resources/js/app.js", + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_BreadCrumbs-D_PGNu06.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_Modal-D-_sV66W.js", - "_DangerButton-DhWljAF_.js" + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Users/Partials/UserPassword.vue": { - "file": "assets/UserPassword-CGZAv6P5.js", + "file": "assets/UserPassword-C4RT8fJf.js", "name": "UserPassword", "src": "resources/js/Pages/Console/Users/Partials/UserPassword.vue", "isDynamicEntry": true, "imports": [ - "_ActionMessage-uxM-o-da.js", - "_Button-Do-BJmWY.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", + "_ActionMessage-Ln0XM8D-.js", + "_Button-D2g1K4pZ.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js" + "_SectionTitle-BuQ5xszV.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Console/Users/Partials/UserProfile.vue": { - "file": "assets/UserProfile-3N4JikwH.js", + "file": "assets/UserProfile-DOPehNaL.js", "name": "UserProfile", "src": "resources/js/Pages/Console/Users/Partials/UserProfile.vue", "isDynamicEntry": true, "imports": [ - "_Button-Do-BJmWY.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", - "_ActionMessage-uxM-o-da.js", - "_SecondaryButton-BucXplhe.js", - "_SelectOrcidId-D9IuGi3f.js", + "_Button-D2g1K4pZ.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", + "_ActionMessage-Ln0XM8D-.js", + "_SecondaryButton-DYbDLKHA.js", + "_SelectOrcidId-Zpq-5XGg.js", + "_RorAffiliationTypeahead-BH6zDoc8.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_LoadingButton-DQ530rQU.js" + "_SectionTitle-BuQ5xszV.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_LoadingButton-Blde9mqi.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Dashboard.vue": { - "file": "assets/Dashboard-CLVWDe8B.js", + "file": "assets/Dashboard-B1liZwWJ.js", "name": "Dashboard", "src": "resources/js/Pages/Dashboard.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Project/Index.vue", - "_StudyCard-CZirq0ik.js", - "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_ToolTip-B-BuMZla.js", - "_use-outside-click-UcI2wRsE.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_hidden-2_Kmyvd6.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_CompoundCards-C7rigkx5.js", + "_ApplicationLogo-Cg0840cQ.js", + "resources/js/app.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_transition-Bxfvum2Z.js", + "_EmptySearchState-D1gGvUOi.js", + "_UsersIcon-BZPR2WVE.js", + "_Pagination-DTxo9TqJ.js", + "_StudyCard-CbFXlVaX.js", + "_index-CIK-HhQI.js", + "_TrashIcon-hldFMpdc.js", + "_UserGroupIcon-v_dx-FoW.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_micro-task-CxIZtCgj.js", - "_switch-DyCbjYu6.js", - "_description-D4q-ya34.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_ShowProjectDates-DEyhF2bt.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_StarIcon-BYJ8kyod.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js", - "_portal-viea1unE.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_Tag-OosEWMYF.js", + "_ShowProjectDates-coZINevl.js", + "_LockClosedIcon-BgJuctHv.js", + "_DocumentTextIcon-55aZskkC.js", + "_ArrowPathIcon-BW1Qv9pG.js", + "_Cog6ToothIcon-CTKrIXtI.js", + "_ShareIcon-Ds-VRaVF.js", + "_StarIcon-DrloavXH.js", + "_Depictor2D-CyOFcT6I.js", + "_portal-FA1zWb63.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" + ] + }, + "resources/js/Pages/Predict.vue": { + "file": "assets/Predict-CMABns-K.js", + "name": "Predict", + "src": "resources/js/Pages/Predict.vue", + "isDynamicEntry": true, + "imports": [ + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js", + "_FlashMessages-Duvc-iuP.js", + "_Footer-DVMJRsC3.js", + "_structureEditor-B2P_9D5s.js", + "_XMarkIcon-JlHVBqMq.js", + "_popover-B4upHQWP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_portal-FA1zWb63.js", + "_use-outside-click-BIOUF4IU.js", + "_hidden-CMJ14BdS.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/PrivacyPolicy.vue": { - "file": "assets/PrivacyPolicy-CAMk5yhX.js", + "file": "assets/PrivacyPolicy-D-s_1Ghv.js", "name": "PrivacyPolicy", "src": "resources/js/Pages/PrivacyPolicy.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_ApplicationLogo-Byie6Ini.js" + "_ApplicationLogo-Cg0840cQ.js", + "_Footer-DVMJRsC3.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Profile/Partials/DeleteUserForm.vue": { - "file": "assets/DeleteUserForm-c8u_GeKx.js", + "file": "assets/DeleteUserForm-DQEcgEri.js", "name": "DeleteUserForm", "src": "resources/js/Pages/Profile/Partials/DeleteUserForm.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_DialogModal-CmZ6e9E-.js", - "_DangerButton-DhWljAF_.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", + "_ActionSection-BdJiuQm5.js", + "_DialogModal-ClgrMrPp.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Profile/Partials/LogoutOtherBrowserSessionsForm.vue": { - "file": "assets/LogoutOtherBrowserSessionsForm-CgVEg4qL.js", + "file": "assets/LogoutOtherBrowserSessionsForm-7eiHSExh.js", "name": "LogoutOtherBrowserSessionsForm", "src": "resources/js/Pages/Profile/Partials/LogoutOtherBrowserSessionsForm.vue", "isDynamicEntry": true, "imports": [ - "_ActionMessage-uxM-o-da.js", - "_ActionSection-DoJrofwP.js", - "_Button-Do-BJmWY.js", - "_DialogModal-CmZ6e9E-.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", + "_ActionMessage-Ln0XM8D-.js", + "_ActionSection-BdJiuQm5.js", + "_Button-D2g1K4pZ.js", + "_DialogModal-ClgrMrPp.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Profile/Partials/TwoFactorAuthenticationForm.vue": { - "file": "assets/TwoFactorAuthenticationForm-Be_MxRp_.js", + "file": "assets/TwoFactorAuthenticationForm-Dqduc_nq.js", "name": "TwoFactorAuthenticationForm", "src": "resources/js/Pages/Profile/Partials/TwoFactorAuthenticationForm.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_Button-Do-BJmWY.js", - "_DialogModal-CmZ6e9E-.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", + "_ActionSection-BdJiuQm5.js", + "_Button-D2g1K4pZ.js", + "_DialogModal-ClgrMrPp.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", "resources/js/app.js", - "_DangerButton-DhWljAF_.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_DangerButton-Ckd3B82Q.js", + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue": { - "file": "assets/UpdatePasswordForm-D8gDPMFW.js", + "file": "assets/UpdatePasswordForm-BwNrnD6f.js", "name": "UpdatePasswordForm", "src": "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue", "isDynamicEntry": true, "imports": [ - "_ActionMessage-uxM-o-da.js", - "_Button-Do-BJmWY.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", + "_ActionMessage-Ln0XM8D-.js", + "_Button-D2g1K4pZ.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js" + "_SectionTitle-BuQ5xszV.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue": { - "file": "assets/UpdateProfileInformationForm-BdjXeRia.js", + "file": "assets/UpdateProfileInformationForm-DHaXaj6d.js", "name": "UpdateProfileInformationForm", "src": "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue", "isDynamicEntry": true, "imports": [ - "_Button-Do-BJmWY.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", - "_ActionMessage-uxM-o-da.js", - "_SecondaryButton-BucXplhe.js", - "_SelectOrcidId-D9IuGi3f.js", + "_Button-D2g1K4pZ.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", + "_ActionMessage-Ln0XM8D-.js", + "_SecondaryButton-DYbDLKHA.js", + "_SelectOrcidId-Zpq-5XGg.js", + "_RorAffiliationTypeahead-BH6zDoc8.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_LoadingButton-DQ530rQU.js" + "_SectionTitle-BuQ5xszV.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_LoadingButton-Blde9mqi.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Profile/Show.vue": { - "file": "assets/Show-CzQxFZ0r.js", + "file": "assets/Show-z90bjzjB.js", "name": "Show", "src": "resources/js/Pages/Profile/Show.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Profile/Partials/DeleteUserForm.vue", - "_SectionBorder-Dz4Jb6r4.js", + "_SectionBorder-9kJNWtL7.js", "resources/js/Pages/Profile/Partials/LogoutOtherBrowserSessionsForm.vue", "resources/js/Pages/Profile/Partials/TwoFactorAuthenticationForm.vue", "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue", "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_ActionSection-DoJrofwP.js", - "_SectionTitle-rK4YId1t.js", - "_Input-B-ZTzbzW.js", - "_ActionMessage-uxM-o-da.js", - "_FormSection-D_YXuO3u.js", - "_Label-DGyfHV0z.js", - "_SelectOrcidId-D9IuGi3f.js", - "_LoadingButton-DQ530rQU.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ActionSection-BdJiuQm5.js", + "_SectionTitle-BuQ5xszV.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_ActionMessage-Ln0XM8D-.js", + "_FormSection-C1YzGodw.js", + "_Label-ChnLJ_WN.js", + "_SelectOrcidId-Zpq-5XGg.js", + "_LoadingButton-Blde9mqi.js", + "_RorAffiliationTypeahead-BH6zDoc8.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Index.vue": { - "file": "assets/Index-BANH-5Nf.js", + "file": "assets/Index-BF0hsom_.js", "name": "Index", "src": "resources/js/Pages/Project/Index.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_Tag-DRPRU5B7.js", - "_ShowProjectDates-DEyhF2bt.js", - "_StarIcon-BYJ8kyod.js", - "_CalendarDaysIcon-bzmmtrVA.js" + "_Tag-OosEWMYF.js", + "_ShowProjectDates-coZINevl.js", + "_TrashIcon-hldFMpdc.js", + "_LockClosedIcon-BgJuctHv.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_DocumentTextIcon-55aZskkC.js", + "_ArrowPathIcon-BW1Qv9pG.js", + "_Cog6ToothIcon-CTKrIXtI.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_ShareIcon-Ds-VRaVF.js", + "_StarIcon-DrloavXH.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Partials/Activity.vue": { - "file": "assets/Activity-HsIibMtO.js", + "file": "assets/Activity-D5iETVRZ.js", "name": "Activity", "src": "resources/js/Pages/Project/Partials/Activity.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_XMarkIcon-BrZ2BUzq.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js" + "_XMarkIcon-JlHVBqMq.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_use-outside-click-BIOUF4IU.js", + "_hidden-CMJ14BdS.js", + "_active-element-history-ElNFAgva.js", + "_description-R20iYW7s.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Partials/Archive.vue": { - "file": "assets/Archive-2S7froJ9.js", + "file": "assets/Archive-BreTP0Hi.js", "name": "Archive", "src": "resources/js/Pages/Project/Partials/Archive.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_DialogModal-CmZ6e9E-.js", - "_DangerButton-DhWljAF_.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", - "_LoadingButton-DQ530rQU.js", + "_ActionSection-BdJiuQm5.js", + "_DialogModal-ClgrMrPp.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", + "_LoadingButton-Blde9mqi.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Partials/Create.vue": { - "file": "assets/Create-uBrHqsep.js", + "file": "assets/Create-uP8lalv9.js", "name": "Create", "src": "resources/js/Pages/Project/Partials/Create.vue", "isDynamicEntry": true, "imports": [ - "_DialogModal-CmZ6e9E-.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", "resources/js/app.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "_Modal-D-_sV66W.js", - "_hidden-2_Kmyvd6.js", - "_use-outside-click-UcI2wRsE.js", - "_micro-task-CxIZtCgj.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_description-D4q-ya34.js" + "_SelectRich-BJqsR0rm.js", + "_switch-BsWHPT9U.js", + "_Modal-CIvNuTYj.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_description-R20iYW7s.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Partials/Delete.vue": { - "file": "assets/Delete-CBncv3vl.js", + "file": "assets/Delete-DNAKW9LT.js", "name": "Delete", "src": "resources/js/Pages/Project/Partials/Delete.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_DialogModal-CmZ6e9E-.js", - "_DangerButton-DhWljAF_.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", - "_LoadingButton-DQ530rQU.js", + "_ActionSection-BdJiuQm5.js", + "_DialogModal-ClgrMrPp.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", + "_LoadingButton-Blde9mqi.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Partials/Details.vue": { - "file": "assets/Details-D72sP5nr.js", + "file": "assets/Details-C3uPrhuQ.js", "name": "Details", "src": "resources/js/Pages/Project/Partials/Details.vue", "isDynamicEntry": true, "imports": [ - "_ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/app.js", - "_InputError-EInikEKW.js", + "_InputError-B8XxIu7k.js", "resources/js/Pages/Project/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_SecondaryButton-BucXplhe.js", - "_SelectRich-Bh8aQhO2.js", - "_Button-Do-BJmWY.js", - "_vue-tags-input-BGwEuaGX.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_XMarkIcon-BrZ2BUzq.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js" + "_style-Ni5A2mqp.js", + "_SecondaryButton-DYbDLKHA.js", + "_SelectRich-BJqsR0rm.js", + "_Button-D2g1K4pZ.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_transition-Bxfvum2Z.js", + "_XMarkIcon-JlHVBqMq.js", + "_portal-FA1zWb63.js", + "_use-outside-click-BIOUF4IU.js", + "_hidden-CMJ14BdS.js", + "_active-element-history-ElNFAgva.js", + "_description-R20iYW7s.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Partials/Restore.vue": { - "file": "assets/Restore-9yIb-R9t.js", + "file": "assets/Restore-DQWzwaRh.js", "name": "Restore", "src": "resources/js/Pages/Project/Partials/Restore.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_DialogModal-CmZ6e9E-.js", - "_SuccessButton-CQHcc1CH.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", - "_LoadingButton-DQ530rQU.js", + "_ActionSection-BdJiuQm5.js", + "_DialogModal-ClgrMrPp.js", + "_SuccessButton-BKHqQWO5.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", + "_LoadingButton-Blde9mqi.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Partials/Unarchive.vue": { - "file": "assets/Unarchive-CGt_XB2P.js", + "file": "assets/Unarchive-NiCxbRjf.js", "name": "Unarchive", "src": "resources/js/Pages/Project/Partials/Unarchive.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_DialogModal-CmZ6e9E-.js", - "_DangerButton-DhWljAF_.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", - "_LoadingButton-DQ530rQU.js", + "_ActionSection-BdJiuQm5.js", + "_DialogModal-ClgrMrPp.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", + "_LoadingButton-Blde9mqi.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Settings.vue": { - "file": "assets/Settings-PrYT47Rc.js", + "file": "assets/Settings-BXsg9ZpU.js", "name": "Settings", "src": "resources/js/Pages/Project/Settings.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/app.js", "resources/js/Pages/Project/Partials/Delete.vue", "resources/js/Pages/Project/Partials/Restore.vue", "resources/js/Pages/Project/Partials/Archive.vue", "resources/js/Pages/Project/Partials/Unarchive.vue", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_InformationCircleIcon-BlXpsvv4.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_ActionSection-DoJrofwP.js", - "_SectionTitle-rK4YId1t.js", - "_Input-B-ZTzbzW.js", - "_LoadingButton-DQ530rQU.js", - "_SuccessButton-CQHcc1CH.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ActionSection-BdJiuQm5.js", + "_SectionTitle-BuQ5xszV.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_LoadingButton-Blde9mqi.js", + "_SuccessButton-BKHqQWO5.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Show.vue": { - "file": "assets/Show-CpO7pd3h.js", + "file": "assets/Show-CM4h_-8H.js", "name": "Show", "src": "resources/js/Pages/Project/Show.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_AccessDialogue-DaBf4ai0.js", + "_AppLayout-czAMl_QX.js", + "_AccessDialogue-D2Qj7WAD.js", "resources/js/app.js", "resources/js/Pages/Study/Index.vue", "resources/js/Pages/Project/Partials/Details.vue", - "_ManageCitation-A3-Z6aBb.js", - "_ToolTip-B-BuMZla.js", - "_Citation-Dj91Ydnp.js", - "_CitationCard-mr9Srs1M.js", - "_DOIBadge-BBho5C2T.js", - "_Tag-DRPRU5B7.js", - "_main-DPI-_6Az.js", - "_ConfirmationModal-BqBcQKy5.js", - "_SecondaryButton-BucXplhe.js", - "_SuccessButton-CQHcc1CH.js", - "_ShowProjectDates-DEyhF2bt.js", - "_StarIcon-BYJ8kyod.js", - "_transition-K5DyIAHH.js", - "_ApplicationLogo-Byie6Ini.js", - "_form-Cn9CuD1E.js", - "_use-outside-click-UcI2wRsE.js", - "_use-text-value-BpD-bIcx.js", - "_hidden-2_Kmyvd6.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ManageCitation-XxXeFAB5.js", + "_ToolTip-BEFzAXXI.js", + "_Citation-m9o49lqa.js", + "_Publish-CArzv3FY.js", + "_AuthorCard-D-sml0gS.js", + "_CitationCard-CAV7zOdT.js", + "_DOIBadge-Cgkqb6zh.js", + "_Tag-OosEWMYF.js", + "_main-Did928Rw.js", + "_ConfirmationModal-D08uc7GZ.js", + "_SecondaryButton-DYbDLKHA.js", + "_SuccessButton-BKHqQWO5.js", + "_ShowProjectDates-coZINevl.js", + "_transition-Bxfvum2Z.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_PencilIcon-CxfIJoOh.js", + "_StarIcon-DrloavXH.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_micro-task-CxIZtCgj.js", - "_switch-DyCbjYu6.js", - "_description-D4q-ya34.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_DangerButton-DhWljAF_.js", - "_ActionMessage-uxM-o-da.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_StudyCard-CZirq0ik.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_StudyCard-CbFXlVaX.js", + "_Depictor2D-CyOFcT6I.js", + "_LockClosedIcon-BgJuctHv.js", "resources/js/Pages/Project/Partials/Activity.vue", - "_portal-viea1unE.js", - "_style-2QlRVEvL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_LoadingButton-DQ530rQU.js", - "_CalendarDaysIcon-bzmmtrVA.js" + "_portal-FA1zWb63.js", + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_LoadingButton-Blde9mqi.js", + "_DangerButton-Ckd3B82Q.js", + "_RorAffiliationTypeahead-BH6zDoc8.js", + "_TrashIcon-DthmeZcs.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Project/Validation.vue": { - "file": "assets/Validation-bZ-nNA82.js", + "file": "assets/Validation-DHX6WagR.js", "name": "Validation", "src": "resources/js/Pages/Project/Validation.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_Validation-racvfLq0.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ChevronUpIcon-CURFCNE8.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Compounds.vue": { - "file": "assets/Compounds-BMz_txSB.js", + "file": "assets/Compounds-CgBDAcSw.js", "name": "Compounds", "src": "resources/js/Pages/Public/Compounds.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "_Depictor2D-BloOWuSj.js", - "resources/js/app.js", - "_StructureSearch-BAHFDTET.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_StructureSearch-CTChAZlj.js", + "_CompoundCards-C7rigkx5.js", + "_Pagination-DTxo9TqJ.js", + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_structureEditor-B2P_9D5s.js", + "_Depictor2D-CyOFcT6I.js" + ], + "css": [ + "assets/Compounds-BQ18YpRg.css", + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Datasets.vue": { - "file": "assets/Datasets-Bk2IgHUa.js", + "file": "assets/Datasets-eQO9Q7uw.js", "name": "Datasets", "src": "resources/js/Pages/Public/Datasets.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_ScaleIcon-CYgZpCW4.js", - "_pickBy-lvn3s5H6.js", - "_Pagination-DUPh-k9M.js", - "_ToolTip-B-BuMZla.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_ScaleIcon-BGti8Jud.js", + "_pickBy-Cq4gBaYP.js", + "_Pagination-DTxo9TqJ.js", + "_Squares2X2Icon-DGk5hsEN.js", + "_QueueListIcon-pHLuzOz3.js", + "_ChevronDownIcon-XdP1J23i.js", + "_UsersIcon-BZPR2WVE.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_TrashIcon-hldFMpdc.js" + ], + "css": [ + "assets/Datasets-CV1Ib_X1.css", + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Embed/Dataset.vue": { - "file": "assets/Dataset-C_iBn6GR.js", + "file": "assets/Dataset-lxmozu5V.js", "name": "Dataset", "src": "resources/js/Pages/Public/Embed/Dataset.vue", "isDynamicEntry": true, "imports": [ - "_SpectraViewer-DAiohBoS.js", + "_SpectraViewer-Cl8c4T6A.js", "resources/js/app.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Embed/Sample.vue": { - "file": "assets/Sample-SpmQRIgb.js", + "file": "assets/Sample-CBjfXtSf.js", "name": "Sample", "src": "resources/js/Pages/Public/Embed/Sample.vue", "isDynamicEntry": true, "imports": [ - "_SpectraViewer-DAiohBoS.js", + "_SpectraViewer-Cl8c4T6A.js", "resources/js/app.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Project/Dataset.vue": { - "file": "assets/Dataset-CpEyPbx5.js", + "file": "assets/Dataset-elBDVWZO.js", "name": "Dataset", "src": "resources/js/Pages/Public/Project/Dataset.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Public/Project/Layout.vue", - "_SpectraViewer-DAiohBoS.js", - "_DOIBadge-BBho5C2T.js", - "_Depictor2D-BloOWuSj.js", - "resources/js/app.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_ShareIcon-Bhy_NH0G.js", - "_ToolTip-B-BuMZla.js", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_PublicDatasetBody-D9fy91sT.js", + "resources/js/app.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_ScaleIcon-CYgZpCW4.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_SeededCoverBackground-CkmszP3E.js", + "_AccessDialogue-D2Qj7WAD.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_SuccessButton-BKHqQWO5.js", + "_main-Did928Rw.js", + "_ChevronRightIcon-DO560peH.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_SpectraViewer-Cl8c4T6A.js", + "_DOIBadge-Cgkqb6zh.js", + "_MolecularInfoPanel-CRODCv3s.js", + "_Depictor2D-CyOFcT6I.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ShareIcon-Ds-VRaVF.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Project/Files.vue": { - "file": "assets/Files-CpkSF9_N.js", + "file": "assets/Files-DMaswFV_.js", "name": "Files", "src": "resources/js/Pages/Public/Project/Files.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Public/Project/Layout.vue", - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_HomeIcon-B3apTAEd.js", - "_DOIBadge-BBho5C2T.js", - "_Tag-DRPRU5B7.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_ScaleIcon-CYgZpCW4.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_FileSystemBrowser-BCIW1ZG6.js", + "resources/js/app.js", + "_HomeIcon-BCWAoLnL.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_SeededCoverBackground-CkmszP3E.js", + "_AccessDialogue-D2Qj7WAD.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_SuccessButton-BKHqQWO5.js", + "_main-Did928Rw.js", + "_ChevronRightIcon-DO560peH.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_ConfirmationModal-D08uc7GZ.js", + "_DangerButton-Ckd3B82Q.js", + "_FileDetails-CtL8Ky_a.js", + "_ToolTip-BEFzAXXI.js", + "_Squares2X2Icon-DdnPGRv-.js", + "_ChevronUpIcon-BvtTCREq.js", + "_ScaleIcon-1OZIOuLg.js", + "_Squares2X2Icon-DGk5hsEN.js", + "_TrashIcon-DthmeZcs.js", + "_ArrowDownTrayIcon-CI9T5GLj.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_EllipsisVerticalIcon-CcAY43ui.js", + "_InformationCircleIcon-BlXpsvv4.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Project/Layout.vue": { - "file": "assets/Layout-DNjK53lj.js", + "file": "assets/Layout-C5z0Co1Z.js", "name": "Layout", "src": "resources/js/Pages/Public/Project/Layout.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_DOIBadge-BBho5C2T.js", - "_Tag-DRPRU5B7.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_ScaleIcon-CYgZpCW4.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_SeededCoverBackground-CkmszP3E.js", + "_AccessDialogue-D2Qj7WAD.js", + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_SuccessButton-BKHqQWO5.js", + "_main-Did928Rw.js", + "resources/js/app.js", + "_ChevronRightIcon-DO560peH.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_UsersIcon-BZPR2WVE.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_Modal-CIvNuTYj.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_TrashIcon-hldFMpdc.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Project/License.vue": { - "file": "assets/License-CIYsdsG1.js", + "file": "assets/License-FsgTnVq0.js", "name": "License", "src": "resources/js/Pages/Public/Project/License.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Public/Project/Layout.vue", "resources/js/app.js", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_DOIBadge-BBho5C2T.js", - "_Tag-DRPRU5B7.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_ScaleIcon-CYgZpCW4.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_SeededCoverBackground-CkmszP3E.js", + "_AccessDialogue-D2Qj7WAD.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_SuccessButton-BKHqQWO5.js", + "_main-Did928Rw.js", + "_ChevronRightIcon-DO560peH.js", + "_ChevronDownIcon-Dv1sFCyB.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Project/Samples.vue": { - "file": "assets/Samples-D9Ch_3Y7.js", + "file": "assets/Samples-qWU3bHD_.js", "name": "Samples", "src": "resources/js/Pages/Public/Project/Samples.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Public/Project/Layout.vue", - "_StudyCardPublic-03l-9aGV.js", - "resources/js/app.js", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "resources/js/Pages/Study/Index.vue", + "_StudyCardPublic-DMMRkFmY.js", + "resources/js/app.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_DOIBadge-BBho5C2T.js", - "_Tag-DRPRU5B7.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_ScaleIcon-CYgZpCW4.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_SeededCoverBackground-CkmszP3E.js", + "_AccessDialogue-D2Qj7WAD.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_SuccessButton-BKHqQWO5.js", + "_main-Did928Rw.js", + "_ChevronRightIcon-DO560peH.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_StudyCard-CbFXlVaX.js", + "_Depictor2D-CyOFcT6I.js", + "_LockClosedIcon-BgJuctHv.js", + "_LockClosedIcon-CAD5UECp.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Project/Show.vue": { - "file": "assets/Show-BAnUmhkW.js", + "file": "assets/Show-BayeheoX.js", "name": "Show", "src": "resources/js/Pages/Public/Project/Show.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Public/Project/Layout.vue", - "_CitationCard-mr9Srs1M.js", - "_index-CwURvEqp.js", - "_Citation-Dj91Ydnp.js", - "resources/js/app.js", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AuthorCard-D-sml0gS.js", + "_CitationCard-CAV7zOdT.js", + "_ManageCitation-XxXeFAB5.js", + "resources/js/Pages/Project/Partials/Details.vue", + "_Publish-CArzv3FY.js", + "_Citation-m9o49lqa.js", + "_DOIBadge-Cgkqb6zh.js", + "_Tag-OosEWMYF.js", + "_index-6dcjgqnm.js", + "resources/js/app.js", + "_PencilIcon-CxfIJoOh.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_DOIBadge-BBho5C2T.js", - "_Tag-DRPRU5B7.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_ScaleIcon-CYgZpCW4.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_SeededCoverBackground-CkmszP3E.js", + "_AccessDialogue-D2Qj7WAD.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_SuccessButton-BKHqQWO5.js", + "_main-Did928Rw.js", + "_ChevronRightIcon-DO560peH.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_TrashIcon-DthmeZcs.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_LoadingButton-Blde9mqi.js", + "_DangerButton-Ckd3B82Q.js", + "_vue-tags-input-CyZTEOCK.js", + "_RorAffiliationTypeahead-BH6zDoc8.js", + "resources/js/Pages/Project/Partials/Activity.vue", + "_style-Ni5A2mqp.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Project/Study.vue": { - "file": "assets/Study-Qa5hdwJy.js", + "file": "assets/Study-C3w-cQpZ.js", "name": "Study", "src": "resources/js/Pages/Public/Project/Study.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Public/Project/Layout.vue", - "_SpectraViewer-DAiohBoS.js", - "_Depictor2D-BloOWuSj.js", - "_Depictor3D-DpHRqJcL.js", - "_DOIBadge-BBho5C2T.js", - "resources/js/app.js", - "_Citation-Dj91Ydnp.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_ShareIcon-Bhy_NH0G.js", - "_ToolTip-B-BuMZla.js", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_SpectraViewer-Cl8c4T6A.js", + "_DOIBadge-Cgkqb6zh.js", + "_MolecularInfoPanel-CRODCv3s.js", + "_Tag-OosEWMYF.js", + "resources/js/app.js", + "_Citation-m9o49lqa.js", + "_CitationCard-CAV7zOdT.js", + "_ShowProjectDates-coZINevl.js", + "_UsersIcon-BZPR2WVE.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ShareIcon-Ds-VRaVF.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_ScaleIcon-CYgZpCW4.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_TrashIcon-hldFMpdc.js", + "_SeededCoverBackground-CkmszP3E.js", + "_AccessDialogue-D2Qj7WAD.js", + "_ActionMessage-Ln0XM8D-.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_SuccessButton-BKHqQWO5.js", + "_main-Did928Rw.js", + "_ChevronRightIcon-DO560peH.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_Depictor2D-CyOFcT6I.js", + "_TrashIcon-DthmeZcs.js", + "_PencilIcon-CxfIJoOh.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Projects.vue": { - "file": "assets/Projects-C0nlvgGM.js", + "file": "assets/Projects-BBz6T6jH.js", "name": "Projects", "src": "resources/js/Pages/Public/Projects.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_pickBy-lvn3s5H6.js", - "_AppLayout-shqwEEI1.js", - "_ProjectCard-C7JLsgIL.js", - "_Pagination-DUPh-k9M.js", - "_ToolTip-B-BuMZla.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_pickBy-Cq4gBaYP.js", + "_AppLayout-czAMl_QX.js", + "_ProjectCard-BV_YGrVn.js", + "_Pagination-DTxo9TqJ.js", + "_Squares2X2Icon-DGk5hsEN.js", + "_QueueListIcon-pHLuzOz3.js", + "_ChevronDownIcon-XdP1J23i.js", + "_UsersIcon-BZPR2WVE.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_ScaleIcon-CYgZpCW4.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_TrashIcon-hldFMpdc.js", + "_Tag-OosEWMYF.js", + "_SeededCoverBackground-CkmszP3E.js", + "_ScaleIcon-BGti8Jud.js", + "_ArrowDownTrayIcon-CI9T5GLj.js", + "_EllipsisVerticalIcon-CcAY43ui.js" + ], + "css": [ + "assets/Projects-Dd3KJlOl.css", + "assets/app-Cb11sZv5.css" + ] + }, + "resources/js/Pages/Public/Sample/Dataset.vue": { + "file": "assets/Dataset-GuZL6Pfm.js", + "name": "Dataset", + "src": "resources/js/Pages/Public/Sample/Dataset.vue", + "isDynamicEntry": true, + "imports": [ + "resources/js/Pages/Public/Sample/Layout.vue", + "_PublicDatasetBody-D9fy91sT.js", + "resources/js/app.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", + "resources/js/Pages/Project/Partials/Create.vue", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_SpectraViewer-Cl8c4T6A.js", + "_DOIBadge-Cgkqb6zh.js", + "_MolecularInfoPanel-CRODCv3s.js", + "_Depictor2D-CyOFcT6I.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ShareIcon-Ds-VRaVF.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Sample/Layout.vue": { - "file": "assets/Layout-DWaWL8_r.js", + "file": "assets/Layout-Bp7ljzI7.js", "name": "Layout", "src": "resources/js/Pages/Public/Sample/Layout.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Sample/Show.vue": { - "file": "assets/Show-CFKNZClV.js", + "file": "assets/Show-BCcvO3Ws.js", "name": "Show", "src": "resources/js/Pages/Public/Sample/Show.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Public/Sample/Layout.vue", - "_SpectraViewer-DAiohBoS.js", - "_Depictor2D-BloOWuSj.js", - "_DOIBadge-BBho5C2T.js", - "resources/js/app.js", - "_Citation-Dj91Ydnp.js", - "_ShowProjectDates-DEyhF2bt.js", - "_Depictor3D-DpHRqJcL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_ShareIcon-Bhy_NH0G.js", - "_ToolTip-B-BuMZla.js", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_SpectraViewer-Cl8c4T6A.js", + "_DOIBadge-Cgkqb6zh.js", + "_MolecularInfoPanel-CRODCv3s.js", + "_Tag-OosEWMYF.js", + "resources/js/app.js", + "_Citation-m9o49lqa.js", + "_ShowProjectDates-coZINevl.js", + "_AuthorCard-D-sml0gS.js", + "_CitationCard-CAV7zOdT.js", + "_UsersIcon-BZPR2WVE.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ShareIcon-Ds-VRaVF.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_CalendarDaysIcon-bzmmtrVA.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_TrashIcon-hldFMpdc.js", + "_Depictor2D-CyOFcT6I.js", + "_TrashIcon-DthmeZcs.js", + "_PencilIcon-CxfIJoOh.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Public/Studies.vue": { - "file": "assets/Studies-DbbilOmo.js", + "file": "assets/Studies-HctkKI0S.js", "name": "Studies", "src": "resources/js/Pages/Public/Studies.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_StudyCardPublic-03l-9aGV.js", - "_pickBy-lvn3s5H6.js", - "_Pagination-DUPh-k9M.js", - "_ToolTip-B-BuMZla.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_StudyCardPublic-DMMRkFmY.js", + "_pickBy-Cq4gBaYP.js", + "_Pagination-DTxo9TqJ.js", + "_Squares2X2Icon-DGk5hsEN.js", + "_QueueListIcon-pHLuzOz3.js", + "_ChevronDownIcon-XdP1J23i.js", + "_UsersIcon-BZPR2WVE.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_TrashIcon-hldFMpdc.js", + "_Depictor2D-CyOFcT6I.js", + "_LockClosedIcon-CAD5UECp.js" + ], + "css": [ + "assets/Studies-BOTB-mZB.css", + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Publish.vue": { - "file": "assets/Publish-DVcN48gb.js", + "file": "assets/Publish-S8-zUNzP.js", "name": "Publish", "src": "resources/js/Pages/Publish.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", - "_vue-tags-input-BGwEuaGX.js", - "_main-DPI-_6Az.js", - "_ManageCitation-A3-Z6aBb.js", - "_CitationCard-mr9Srs1M.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js", - "_SelectRich-Bh8aQhO2.js", - "_ToggleButton-C0L2QH2i.js", - "_index-CwURvEqp.js", - "_ConfirmationModal-BqBcQKy5.js", - "_SuccessButton-CQHcc1CH.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", + "_vue-tags-input-CyZTEOCK.js", + "_Validation-racvfLq0.js", + "_main-Did928Rw.js", + "_ManageCitation-XxXeFAB5.js", + "_AuthorCard-D-sml0gS.js", + "_Depictor2D-CyOFcT6I.js", + "_LockClosedIcon-CAD5UECp.js", + "_SelectRich-BJqsR0rm.js", + "_CitationCard-CAV7zOdT.js", + "_index-6dcjgqnm.js", + "_ConfirmationModal-D08uc7GZ.js", + "_SuccessButton-BKHqQWO5.js", + "_ChevronUpIcon-BvtTCREq.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_FlashMessages-Duvc-iuP.js", + "_UserGroupIcon-v_dx-FoW.js", + "_DocumentTextIcon-55aZskkC.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_Button-Do-BJmWY.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_DangerButton-DhWljAF_.js", - "_LoadingButton-DQ530rQU.js", - "_Tag-DRPRU5B7.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_Button-D2g1K4pZ.js", + "_switch-BsWHPT9U.js", + "_form-B25fLBcn.js", + "_hidden-CMJ14BdS.js", + "_description-R20iYW7s.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_use-text-value-ZZ3p0KKx.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ChevronUpIcon-CURFCNE8.js", + "_LoadingButton-Blde9mqi.js", + "_DangerButton-Ckd3B82Q.js", + "_RorAffiliationTypeahead-BH6zDoc8.js", + "_Input-DWYxV1Iv.js", + "_TrashIcon-DthmeZcs.js", + "_PencilIcon-CxfIJoOh.js", + "_Tag-OosEWMYF.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_CheckCircleIcon-BZwKPOjy.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Recent.vue": { - "file": "assets/Recent-TJku6q1i.js", + "file": "assets/Recent-CLFa9iyE.js", "name": "Recent", "src": "resources/js/Pages/Recent.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Project/Index.vue", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_ShowProjectDates-DEyhF2bt.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_StarIcon-BYJ8kyod.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_Tag-OosEWMYF.js", + "_ShowProjectDates-coZINevl.js", + "_LockClosedIcon-BgJuctHv.js", + "_DocumentTextIcon-55aZskkC.js", + "_ArrowPathIcon-BW1Qv9pG.js", + "_Cog6ToothIcon-CTKrIXtI.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_ShareIcon-Ds-VRaVF.js", + "_StarIcon-DrloavXH.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/SharedWithMe.vue": { - "file": "assets/SharedWithMe-fWP6Yees.js", + "file": "assets/SharedWithMe-DUKxB8Ai.js", "name": "SharedWithMe", "src": "resources/js/Pages/SharedWithMe.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Project/Index.vue", - "_StudyCard-CZirq0ik.js", - "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_StudyCard-CbFXlVaX.js", + "resources/js/app.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_ShowProjectDates-DEyhF2bt.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_StarIcon-BYJ8kyod.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_Tag-OosEWMYF.js", + "_ShowProjectDates-coZINevl.js", + "_LockClosedIcon-BgJuctHv.js", + "_DocumentTextIcon-55aZskkC.js", + "_ArrowPathIcon-BW1Qv9pG.js", + "_Cog6ToothIcon-CTKrIXtI.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_ShareIcon-Ds-VRaVF.js", + "_StarIcon-DrloavXH.js", + "_Depictor2D-CyOFcT6I.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Starred.vue": { - "file": "assets/Starred-AO7GGajB.js", + "file": "assets/Starred-BV4cBAsg.js", "name": "Starred", "src": "resources/js/Pages/Starred.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Project/Index.vue", "resources/js/app.js", - "_StudyCard-CZirq0ik.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_StudyCard-CbFXlVaX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_ShowProjectDates-DEyhF2bt.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_StarIcon-BYJ8kyod.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_Tag-OosEWMYF.js", + "_ShowProjectDates-coZINevl.js", + "_LockClosedIcon-BgJuctHv.js", + "_DocumentTextIcon-55aZskkC.js", + "_ArrowPathIcon-BW1Qv9pG.js", + "_Cog6ToothIcon-CTKrIXtI.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_ShareIcon-Ds-VRaVF.js", + "_StarIcon-DrloavXH.js", + "_Depictor2D-CyOFcT6I.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/About.vue": { - "file": "assets/About-CSm3066P.js", + "file": "assets/About-DDXIWrdn.js", "name": "About", "src": "resources/js/Pages/Study/About.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Study/Content.vue", - "_AppLayout-shqwEEI1.js", + "_vue3-slider.common-DgsdEvX8.js", "resources/js/app.js", - "_ToolTip-B-BuMZla.js", - "_InputError-EInikEKW.js", - "_Depictor2D-BloOWuSj.js", + "_structureEditor-B2P_9D5s.js", + "_ToolTip-BEFzAXXI.js", + "_InputError-B8XxIu7k.js", + "_Depictor2D-CyOFcT6I.js", + "_AuthorCard-D-sml0gS.js", + "_InformationCircleIcon-BlXpsvv4.js", + "_PencilIcon-CxfIJoOh.js", + "_TrashIcon-DthmeZcs.js", "resources/js/Pages/Study/Layout.vue", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", + "resources/js/Pages/Project/Partials/Create.vue", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_XMarkIcon-BrZ2BUzq.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_style-2QlRVEvL.js", - "_Button-Do-BJmWY.js", - "_SelectRich-Bh8aQhO2.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_SecondaryButton-BucXplhe.js", - "_vue-tags-input-BGwEuaGX.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_CircleStackIcon-NWc2meE7.js", - "_ApplicationLogo-Byie6Ini.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", - "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js", + "_Tag-OosEWMYF.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Content.vue": { - "file": "assets/Content-D9WfmHUt.js", + "file": "assets/Content-B4Z5g_bz.js", "name": "Content", "src": "resources/js/Pages/Study/Content.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Study/Layout.vue", "resources/js/app.js", - "_CircleStackIcon-NWc2meE7.js", - "_AppLayout-shqwEEI1.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", + "resources/js/Pages/Project/Partials/Create.vue", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", - "_InputError-EInikEKW.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_XMarkIcon-BrZ2BUzq.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_style-2QlRVEvL.js", - "_Button-Do-BJmWY.js", - "_SelectRich-Bh8aQhO2.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_SecondaryButton-BucXplhe.js", - "_vue-tags-input-BGwEuaGX.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_ToolTip-B-BuMZla.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_ApplicationLogo-Byie6Ini.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", - "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Datasets.vue": { - "file": "assets/Datasets-DkPsuWgp.js", + "file": "assets/Datasets-C3fOFsdC.js", "name": "Datasets", "src": "resources/js/Pages/Study/Datasets.vue", "isDynamicEntry": true, "imports": [ "resources/js/Pages/Study/Content.vue", - "_LoadingButton-DQ530rQU.js", - "_SpectraViewer-DAiohBoS.js", - "_AppLayout-shqwEEI1.js", + "_LoadingButton-Blde9mqi.js", + "_SpectraViewer-Cl8c4T6A.js", + "_SpectraEditor-DGo8KwaT.js", "resources/js/app.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_ShareIcon-Bhy_NH0G.js", - "_ToolTip-B-BuMZla.js", + "_UsersIcon-BZPR2WVE.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ShareIcon-Ds-VRaVF.js", "resources/js/Pages/Study/Layout.vue", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", + "resources/js/Pages/Project/Partials/Create.vue", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", - "_InputError-EInikEKW.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_XMarkIcon-BrZ2BUzq.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_style-2QlRVEvL.js", - "_Button-Do-BJmWY.js", - "_SelectRich-Bh8aQhO2.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_SecondaryButton-BucXplhe.js", - "_vue-tags-input-BGwEuaGX.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_ApplicationLogo-Byie6Ini.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", - "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_CircleStackIcon-NWc2meE7.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_ArrowPathIcon-BW1Qv9pG.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Files.vue": { - "file": "assets/Files-BM_oQcaU.js", + "file": "assets/Files-DkLp3kEJ.js", "name": "Files", "src": "resources/js/Pages/Study/Files.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_FileDetails-CtL8Ky_a.js", "resources/js/app.js", "resources/js/Pages/Study/Content.vue", - "_ToolTip-B-BuMZla.js", - "_HomeIcon-B3apTAEd.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", - "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", + "_ArrowDownTrayIcon-CI9T5GLj.js", + "_ChevronDownIcon-XdP1J23i.js", + "_ChevronUpIcon-CURFCNE8.js", + "_Squares2X2Icon-DGk5hsEN.js", + "_HomeIcon-BCWAoLnL.js", "resources/js/Pages/Study/Layout.vue", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", + "resources/js/Pages/Project/Partials/Create.vue", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_CircleStackIcon-NWc2meE7.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Index.vue": { - "file": "assets/Index-C82ZXMLZ.js", + "file": "assets/Index-bNrn4rjs.js", "name": "Index", "src": "resources/js/Pages/Study/Index.vue", "isDynamicEntry": true, "imports": [ - "_StudyCard-CZirq0ik.js", + "_StudyCard-CbFXlVaX.js", "resources/js/app.js", - "_Depictor2D-BloOWuSj.js", - "_LockClosedIcon-CK-FFoS7.js" + "_Depictor2D-CyOFcT6I.js", + "_LockClosedIcon-BgJuctHv.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Integrations.vue": { - "file": "assets/Integrations-CAsr5-W8.js", + "file": "assets/Integrations-DaM96PXp.js", "name": "Integrations", "src": "resources/js/Pages/Study/Integrations.vue", "isDynamicEntry": true, @@ -2562,92 +3287,101 @@ "resources/js/Pages/Study/Content.vue", "resources/js/app.js", "resources/js/Pages/Study/Layout.vue", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_CircleStackIcon-NWc2meE7.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Layout.vue": { - "file": "assets/Layout-DaLtTPjz.js", + "file": "assets/Layout-C47IPESL.js", "name": "Layout", "src": "resources/js/Pages/Study/Layout.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/app.js", "resources/js/Pages/Study/Partials/Details.vue", - "_AccessDialogue-DaBf4ai0.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_ActionMessage-uxM-o-da.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/MolecularIdentifications.vue": { - "file": "assets/MolecularIdentifications-DeOIcevJ.js", + "file": "assets/MolecularIdentifications-CPmysiPb.js", "name": "MolecularIdentifications", "src": "resources/js/Pages/Study/MolecularIdentifications.vue", "isDynamicEntry": true, @@ -2655,47 +3389,52 @@ "resources/js/Pages/Study/Content.vue", "resources/js/app.js", "resources/js/Pages/Study/Layout.vue", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_CircleStackIcon-NWc2meE7.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Notifications.vue": { - "file": "assets/Notifications-Cyza3xAp.js", + "file": "assets/Notifications-CkSfUr8Q.js", "name": "Notifications", "src": "resources/js/Pages/Study/Notifications.vue", "isDynamicEntry": true, @@ -2703,129 +3442,147 @@ "resources/js/Pages/Study/Content.vue", "resources/js/app.js", "resources/js/Pages/Study/Layout.vue", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_CircleStackIcon-NWc2meE7.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Partials/Activity.vue": { - "file": "assets/Activity-BHNnVIIB.js", + "file": "assets/Activity-BbyIsCHA.js", "name": "Activity", "src": "resources/js/Pages/Study/Partials/Activity.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_XMarkIcon-BrZ2BUzq.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js" + "_XMarkIcon-JlHVBqMq.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_use-outside-click-BIOUF4IU.js", + "_hidden-CMJ14BdS.js", + "_active-element-history-ElNFAgva.js", + "_description-R20iYW7s.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Partials/Create.vue": { - "file": "assets/Create-BVLvjQxD.js", + "file": "assets/Create-mPIigJoM.js", "name": "Create", "src": "resources/js/Pages/Study/Partials/Create.vue", "isDynamicEntry": true, "imports": [ - "_DialogModal-CmZ6e9E-.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", + "_DialogModal-ClgrMrPp.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", "resources/js/app.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "_Modal-D-_sV66W.js", - "_hidden-2_Kmyvd6.js", - "_use-outside-click-UcI2wRsE.js", - "_micro-task-CxIZtCgj.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_description-D4q-ya34.js" + "_SelectRich-BJqsR0rm.js", + "_switch-BsWHPT9U.js", + "_Modal-CIvNuTYj.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_description-R20iYW7s.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Partials/Delete.vue": { - "file": "assets/Delete-uegnL0wb.js", + "file": "assets/Delete-DnysXRkS.js", "name": "Delete", "src": "resources/js/Pages/Study/Partials/Delete.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_DialogModal-CmZ6e9E-.js", - "_DangerButton-DhWljAF_.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", + "_ActionSection-BdJiuQm5.js", + "_DialogModal-ClgrMrPp.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Partials/Details.vue": { - "file": "assets/Details-BAAPFtXS.js", + "file": "assets/Details-By8QgARd.js", "name": "Details", "src": "resources/js/Pages/Study/Partials/Details.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_ActionMessage-uxM-o-da.js", - "_InputError-EInikEKW.js", + "_ActionMessage-Ln0XM8D-.js", + "_InputError-B8XxIu7k.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_Button-Do-BJmWY.js", - "_SelectRich-Bh8aQhO2.js", - "_SecondaryButton-BucXplhe.js", - "_vue-tags-input-BGwEuaGX.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_XMarkIcon-BrZ2BUzq.js", - "_transition-K5DyIAHH.js", - "_hidden-2_Kmyvd6.js", - "_use-outside-click-UcI2wRsE.js", - "_micro-task-CxIZtCgj.js", - "_form-Cn9CuD1E.js", - "_use-text-value-BpD-bIcx.js", - "_portal-viea1unE.js", - "_description-D4q-ya34.js" + "_style-Ni5A2mqp.js", + "_Button-D2g1K4pZ.js", + "_SelectRich-BJqsR0rm.js", + "_SecondaryButton-DYbDLKHA.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_XMarkIcon-JlHVBqMq.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_transition-Bxfvum2Z.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_portal-FA1zWb63.js", + "_description-R20iYW7s.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Protocols.vue": { - "file": "assets/Protocols-0MJ994Qs.js", + "file": "assets/Protocols-Bqd3zqZ5.js", "name": "Protocols", "src": "resources/js/Pages/Study/Protocols.vue", "isDynamicEntry": true, @@ -2833,360 +3590,441 @@ "resources/js/Pages/Study/Content.vue", "resources/js/app.js", "resources/js/Pages/Study/Layout.vue", - "_AppLayout-shqwEEI1.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", "resources/js/Pages/Study/Partials/Details.vue", - "_ActionMessage-uxM-o-da.js", + "_ActionMessage-Ln0XM8D-.js", "resources/js/Pages/Study/Partials/Activity.vue", - "_style-2QlRVEvL.js", - "_ClipboardDocumentIcon-DCnLMHiO.js", - "_AccessDialogue-DaBf4ai0.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js", - "_Citation-Dj91Ydnp.js", - "_StarIcon-BYJ8kyod.js", - "_CircleStackIcon-NWc2meE7.js" + "_style-Ni5A2mqp.js", + "_vue-tags-input-CyZTEOCK.js", + "_ClipboardDocumentIcon-BvbagfVF.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_AccessDialogue-D2Qj7WAD.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js", + "_ChevronDownIcon-XdP1J23i.js", + "_Citation-m9o49lqa.js", + "_StarIcon-DrloavXH.js", + "_CircleStackIcon-CwNgBqYe.js", + "_Squares2X2Icon-DdnPGRv-.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Study/Settings.vue": { - "file": "assets/Settings-DJePdnOZ.js", + "file": "assets/Settings-CoxTQZhu.js", "name": "Settings", "src": "resources/js/Pages/Study/Settings.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/app.js", "resources/js/Pages/Study/Partials/Delete.vue", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_ActionSection-DoJrofwP.js", - "_SectionTitle-rK4YId1t.js", - "_Input-B-ZTzbzW.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ActionSection-BdJiuQm5.js", + "_SectionTitle-BuQ5xszV.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Teams/Create.vue": { - "file": "assets/Create-QXAAHtHo.js", + "file": "assets/Create-CYwkEvzb.js", "name": "Create", "src": "resources/js/Pages/Teams/Create.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Teams/Partials/CreateTeamForm.vue", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_FormSection-D_YXuO3u.js", - "_SectionTitle-rK4YId1t.js", - "_Input-B-ZTzbzW.js", - "_Label-DGyfHV0z.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_FormSection-C1YzGodw.js", + "_SectionTitle-BuQ5xszV.js", + "_Input-DWYxV1Iv.js", + "_Label-ChnLJ_WN.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Teams/Partials/CreateTeamForm.vue": { - "file": "assets/CreateTeamForm-DrMhQhpS.js", + "file": "assets/CreateTeamForm-CISUNm4z.js", "name": "CreateTeamForm", "src": "resources/js/Pages/Teams/Partials/CreateTeamForm.vue", "isDynamicEntry": true, "imports": [ - "_Button-Do-BJmWY.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", + "_Button-D2g1K4pZ.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js" + "_SectionTitle-BuQ5xszV.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Teams/Partials/DeleteTeamForm.vue": { - "file": "assets/DeleteTeamForm-X8EJMnBB.js", + "file": "assets/DeleteTeamForm-CW7azKhz.js", "name": "DeleteTeamForm", "src": "resources/js/Pages/Teams/Partials/DeleteTeamForm.vue", "isDynamicEntry": true, "imports": [ - "_ActionSection-DoJrofwP.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_SecondaryButton-BucXplhe.js", - "_InputError-EInikEKW.js", - "_Input-B-ZTzbzW.js", + "_ActionSection-BdJiuQm5.js", + "_ConfirmationModal-D08uc7GZ.js", + "_DangerButton-Ckd3B82Q.js", + "_SecondaryButton-DYbDLKHA.js", + "_InputError-B8XxIu7k.js", + "_Input-DWYxV1Iv.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Teams/Partials/TeamMemberManager.vue": { - "file": "assets/TeamMemberManager-BJHjubQm.js", + "file": "assets/TeamMemberManager-o8LB8c-Z.js", "name": "TeamMemberManager", "src": "resources/js/Pages/Teams/Partials/TeamMemberManager.vue", "isDynamicEntry": true, "imports": [ - "_ActionMessage-uxM-o-da.js", - "_ActionSection-DoJrofwP.js", - "_Button-Do-BJmWY.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_DialogModal-CmZ6e9E-.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", - "_SecondaryButton-BucXplhe.js", - "_SectionBorder-Dz4Jb6r4.js", + "_ActionMessage-Ln0XM8D-.js", + "_ActionSection-BdJiuQm5.js", + "_Button-D2g1K4pZ.js", + "_ConfirmationModal-D08uc7GZ.js", + "_DangerButton-Ckd3B82Q.js", + "_DialogModal-ClgrMrPp.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", + "_SecondaryButton-DYbDLKHA.js", + "_SectionBorder-9kJNWtL7.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js", - "_Modal-D-_sV66W.js" + "_SectionTitle-BuQ5xszV.js", + "_Modal-CIvNuTYj.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Teams/Partials/UpdateTeamNameForm.vue": { - "file": "assets/UpdateTeamNameForm-Cx6F-aRP.js", + "file": "assets/UpdateTeamNameForm-BoWDj6bd.js", "name": "UpdateTeamNameForm", "src": "resources/js/Pages/Teams/Partials/UpdateTeamNameForm.vue", "isDynamicEntry": true, "imports": [ - "_ActionMessage-uxM-o-da.js", - "_Button-Do-BJmWY.js", - "_FormSection-D_YXuO3u.js", - "_Input-B-ZTzbzW.js", - "_InputError-EInikEKW.js", - "_Label-DGyfHV0z.js", + "_ActionMessage-Ln0XM8D-.js", + "_Button-D2g1K4pZ.js", + "_FormSection-C1YzGodw.js", + "_Input-DWYxV1Iv.js", + "_InputError-B8XxIu7k.js", + "_Label-ChnLJ_WN.js", "resources/js/app.js", - "_SectionTitle-rK4YId1t.js" + "_SectionTitle-BuQ5xszV.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Teams/Show.vue": { - "file": "assets/Show-CcKcY4qN.js", + "file": "assets/Show-DTHdIl0y.js", "name": "Show", "src": "resources/js/Pages/Teams/Show.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Teams/Partials/DeleteTeamForm.vue", - "_SectionBorder-Dz4Jb6r4.js", + "_SectionBorder-9kJNWtL7.js", "resources/js/Pages/Teams/Partials/TeamMemberManager.vue", "resources/js/Pages/Teams/Partials/UpdateTeamNameForm.vue", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_ActionSection-DoJrofwP.js", - "_SectionTitle-rK4YId1t.js", - "_Input-B-ZTzbzW.js", - "_ActionMessage-uxM-o-da.js", - "_FormSection-D_YXuO3u.js", - "_Label-DGyfHV0z.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ActionSection-BdJiuQm5.js", + "_SectionTitle-BuQ5xszV.js", + "_ConfirmationModal-D08uc7GZ.js", + "_DangerButton-Ckd3B82Q.js", + "_Input-DWYxV1Iv.js", + "_ActionMessage-Ln0XM8D-.js", + "_FormSection-C1YzGodw.js", + "_Label-ChnLJ_WN.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/TermsOfService.vue": { - "file": "assets/TermsOfService-DImG-bQc.js", + "file": "assets/TermsOfService-Dz1VMk76.js", "name": "TermsOfService", "src": "resources/js/Pages/TermsOfService.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_AuthenticationCardLogo-B3Uq11MZ.js", - "_ApplicationLogo-Byie6Ini.js" + "_ApplicationLogo-Cg0840cQ.js", + "_Footer-DVMJRsC3.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Trashed.vue": { - "file": "assets/Trashed-WZAbWqOM.js", + "file": "assets/Trashed-Ci6CpKBc.js", "name": "Trashed", "src": "resources/js/Pages/Trashed.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", + "_AppLayout-czAMl_QX.js", "resources/js/Pages/Project/Index.vue", "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_DialogModal-CmZ6e9E-.js", - "_Modal-D-_sV66W.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_InputError-EInikEKW.js", - "_SelectRich-Bh8aQhO2.js", - "_switch-DyCbjYu6.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_vue-tags-input-BGwEuaGX.js", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js", - "_Tag-DRPRU5B7.js", - "_ShowProjectDates-DEyhF2bt.js", - "_CalendarDaysIcon-bzmmtrVA.js", - "_StarIcon-BYJ8kyod.js" + "_DialogModal-ClgrMrPp.js", + "_Modal-CIvNuTYj.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_InputError-B8XxIu7k.js", + "_SelectRich-BJqsR0rm.js", + "_hidden-CMJ14BdS.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_form-B25fLBcn.js", + "_use-text-value-ZZ3p0KKx.js", + "_switch-BsWHPT9U.js", + "_description-R20iYW7s.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_Tag-OosEWMYF.js", + "_ShowProjectDates-coZINevl.js", + "_LockClosedIcon-BgJuctHv.js", + "_DocumentTextIcon-55aZskkC.js", + "_ArrowPathIcon-BW1Qv9pG.js", + "_Cog6ToothIcon-CTKrIXtI.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_ShareIcon-Ds-VRaVF.js", + "_StarIcon-DrloavXH.js" + ], + "css": [ + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Upload.vue": { - "file": "assets/Upload-4gCobcYZ.js", + "file": "assets/Upload-8N3xzaBv.js", "name": "Upload", "src": "resources/js/Pages/Upload.vue", "isDynamicEntry": true, "imports": [ - "_AppLayout-shqwEEI1.js", - "resources/js/app.js", - "_InputError-EInikEKW.js", - "_SecondaryButton-BucXplhe.js", - "_Button-Do-BJmWY.js", - "_DialogModal-CmZ6e9E-.js", - "_Depictor2D-BloOWuSj.js", - "_Depictor3D-DpHRqJcL.js", - "_vue-tags-input-BGwEuaGX.js", - "_index-CwURvEqp.js", - "_SelectRich-Bh8aQhO2.js", - "_ApplicationLogo-Byie6Ini.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_ToolTip-B-BuMZla.js", - "_use-text-value-BpD-bIcx.js", - "_form-Cn9CuD1E.js", - "_XMarkIcon-BrZ2BUzq.js", - "_FlashMessages-H7Dz0t6O.js", - "_AnnouncementBanner-B_CIbGMm.js", + "_AppLayout-czAMl_QX.js", + "resources/js/app.js", + "_InputError-B8XxIu7k.js", + "_SecondaryButton-DYbDLKHA.js", + "_Button-D2g1K4pZ.js", + "_DialogModal-ClgrMrPp.js", + "_Cog6ToothIcon-CTKrIXtI.js", + "_ArrowTopRightOnSquareIcon-fRg9u6y6.js", + "_FileSystemBrowser-BCIW1ZG6.js", + "_Validation-racvfLq0.js", + "_EmptySearchState-D1gGvUOi.js", + "_SpectraEditor-DGo8KwaT.js", + "_Depictor2D-CyOFcT6I.js", + "_structureEditor-B2P_9D5s.js", + "_vue3-slider.common-DgsdEvX8.js", + "_vue-tags-input-CyZTEOCK.js", + "_index-6dcjgqnm.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_ChevronRightIcon-DO560peH.js", + "_ExclamationCircleIcon-Bk-CmCjh.js", + "_SelectRich-BJqsR0rm.js", + "_InformationCircleIcon-BlXpsvv4.js", + "_PencilIcon-CxfIJoOh.js", + "_TrashIcon-DthmeZcs.js", + "_ApplicationLogo-Cg0840cQ.js", + "_XMarkIcon-JlHVBqMq.js", + "_FlashMessages-Duvc-iuP.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_AnnouncementBanner-B6cY8Bsi.js", "resources/js/Pages/Project/Partials/Create.vue", - "_switch-DyCbjYu6.js", - "_Modal-D-_sV66W.js", - "resources/js/Pages/Study/Partials/Create.vue", - "_ConfirmationModal-BqBcQKy5.js", - "_DangerButton-DhWljAF_.js" + "_switch-BsWHPT9U.js", + "_form-B25fLBcn.js", + "_hidden-CMJ14BdS.js", + "_description-R20iYW7s.js", + "_Modal-CIvNuTYj.js", + "_use-outside-click-BIOUF4IU.js", + "_active-element-history-ElNFAgva.js", + "_use-text-value-ZZ3p0KKx.js", + "_transition-Bxfvum2Z.js", + "_portal-FA1zWb63.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_ConfirmationModal-D08uc7GZ.js", + "_DangerButton-Ckd3B82Q.js", + "_FileDetails-CtL8Ky_a.js", + "_ToolTip-BEFzAXXI.js", + "_Squares2X2Icon-DdnPGRv-.js", + "_ChevronUpIcon-BvtTCREq.js", + "_ScaleIcon-1OZIOuLg.js", + "_Squares2X2Icon-DGk5hsEN.js", + "_ArrowDownTrayIcon-CI9T5GLj.js", + "_EllipsisVerticalIcon-CcAY43ui.js", + "_ChevronUpIcon-CURFCNE8.js", + "_ArrowPathIcon-BW1Qv9pG.js" + ], + "css": [ + "assets/Upload-BbvVozt2.css", + "assets/app-Cb11sZv5.css" ] }, "resources/js/Pages/Welcome.vue": { - "file": "assets/Welcome-Dr7UzoV3.js", + "file": "assets/Welcome-BRO-f0Zg.js", "name": "Welcome", "src": "resources/js/Pages/Welcome.vue", "isDynamicEntry": true, "imports": [ "resources/js/app.js", - "_ApplicationLogo-Byie6Ini.js", - "_ProjectCard-C7JLsgIL.js", - "_StructureSearch-BAHFDTET.js", - "_ToolTip-B-BuMZla.js", - "_FlashMessages-H7Dz0t6O.js", - "_CircleStackIcon-NWc2meE7.js", - "_XMarkIcon-BrZ2BUzq.js", - "_MagnifyingGlassIcon-Cq7Ou0Fo.js", - "_Tag-DRPRU5B7.js", - "_ScaleIcon-CYgZpCW4.js", - "_transition-K5DyIAHH.js", - "_portal-viea1unE.js", - "_use-outside-click-UcI2wRsE.js", - "_hidden-2_Kmyvd6.js", - "_micro-task-CxIZtCgj.js", - "_description-D4q-ya34.js", - "_use-text-value-BpD-bIcx.js" + "_ApplicationLogo-Cg0840cQ.js", + "_ProjectCard-BV_YGrVn.js", + "_Footer-DVMJRsC3.js", + "_StructureSearch-CTChAZlj.js", + "_index-CIK-HhQI.js", + "_structureEditor-B2P_9D5s.js", + "_MagnifyingGlassIcon-Dp6MxRhw.js", + "_transition-Bxfvum2Z.js", + "_ToolTip-BEFzAXXI.js", + "_ChevronDownIcon-Dv1sFCyB.js", + "_FlashMessages-Duvc-iuP.js", + "_LockClosedIcon-BgJuctHv.js", + "_UserGroupIcon-v_dx-FoW.js", + "_CircleStackIcon-CwNgBqYe.js", + "_ChevronDownIcon-XdP1J23i.js", + "_popover-B4upHQWP.js", + "_ScaleIcon-1OZIOuLg.js", + "_XMarkIcon-JlHVBqMq.js", + "_UsersIcon-BZPR2WVE.js", + "_TrashIcon-hldFMpdc.js", + "_DocumentTextIcon-55aZskkC.js", + "_Tag-OosEWMYF.js", + "_SeededCoverBackground-CkmszP3E.js", + "_ScaleIcon-BGti8Jud.js", + "_ArrowDownTrayIcon-CI9T5GLj.js", + "_EllipsisVerticalIcon-CcAY43ui.js", + "_portal-FA1zWb63.js", + "_use-outside-click-BIOUF4IU.js", + "_hidden-CMJ14BdS.js", + "_active-element-history-ElNFAgva.js", + "_description-R20iYW7s.js", + "_CheckCircleIcon-BZwKPOjy.js", + "_use-text-value-ZZ3p0KKx.js" + ], + "css": [ + "assets/Welcome-D7WYSUTI.css", + "assets/app-Cb11sZv5.css" ] }, "resources/js/app.js": { - "file": "assets/app-B0IVowKw.js", + "file": "assets/app-CiYmDbO7.js", "name": "app", "src": "resources/js/app.js", "isEntry": true, + "isDynamicEntry": true, "dynamicImports": [ "resources/js/Pages/API/Index.vue", "resources/js/Pages/API/Partials/ApiTokenManager.vue", @@ -3211,6 +4049,7 @@ "resources/js/Pages/Console/Users/Partials/UserPassword.vue", "resources/js/Pages/Console/Users/Partials/UserProfile.vue", "resources/js/Pages/Dashboard.vue", + "resources/js/Pages/Predict.vue", "resources/js/Pages/PrivacyPolicy.vue", "resources/js/Pages/Profile/Partials/DeleteUserForm.vue", "resources/js/Pages/Profile/Partials/LogoutOtherBrowserSessionsForm.vue", @@ -3241,6 +4080,7 @@ "resources/js/Pages/Public/Project/Show.vue", "resources/js/Pages/Public/Project/Study.vue", "resources/js/Pages/Public/Projects.vue", + "resources/js/Pages/Public/Sample/Dataset.vue", "resources/js/Pages/Public/Sample/Layout.vue", "resources/js/Pages/Public/Sample/Show.vue", "resources/js/Pages/Public/Studies.vue", @@ -3275,7 +4115,10 @@ "resources/js/Pages/Welcome.vue" ], "css": [ - "assets/app-DisLJtzg.css" + "assets/app-Cb11sZv5.css" + ], + "assets": [ + "assets/resources-D5ymtJwZ.json" ] } } \ No newline at end of file diff --git a/public/css/app.css b/public/css/app.css index 185d78193..e9caeb677 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -1,6 +1,3 @@ -/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com*//*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */html{-webkit-text-size-adjust:100%;line-height:1.15;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;margin:0}hr{color:inherit;height:0}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}:-moz-focusring{outline:1px dotted ButtonText}:-moz-ui-invalid{box-shadow:none}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:Nunito,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{font-family:inherit;line-height:inherit}*,:after,:before{border:0 solid;box-sizing:border-box}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:-moz-focusring{outline:auto}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{color:inherit;line-height:inherit;padding:0}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-border-opacity:1;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-blur:var(--tw-empty,/*!*/ /*!*/);--tw-brightness:var(--tw-empty,/*!*/ /*!*/);--tw-contrast:var(--tw-empty,/*!*/ /*!*/);--tw-grayscale:var(--tw-empty,/*!*/ /*!*/);--tw-hue-rotate:var(--tw-empty,/*!*/ /*!*/);--tw-invert:var(--tw-empty,/*!*/ /*!*/);--tw-saturate:var(--tw-empty,/*!*/ /*!*/);--tw-sepia:var(--tw-empty,/*!*/ /*!*/);--tw-drop-shadow:var(--tw-empty,/*!*/ /*!*/);--tw-filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);--tw-backdrop-blur:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-brightness:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-contrast:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-grayscale:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-hue-rotate:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-invert:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-opacity:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-saturate:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-sepia:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);border-color:rgba(229,231,235,var(--tw-border-opacity))}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);outline:2px solid transparent;outline-offset:2px}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}select{-webkit-print-color-adjust:exact;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;color-adjust:exact;padding-right:2.5rem}[multiple]{-webkit-print-color-adjust:unset;background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;color-adjust:unset;padding-right:.75rem}[type=checkbox],[type=radio]{-webkit-print-color-adjust:exact;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;color-adjust:exact;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;width:1rem}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);outline:2px solid transparent;outline-offset:2px}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:transparent}[type=checkbox]:indeterminate{background-color:currentColor;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:transparent}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px auto -webkit-focus-ring-color}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.prose{color:#374151;max-width:65ch}.prose [class~=lead]{color:#4b5563;font-size:1.25em;line-height:1.6;margin-bottom:1.2em;margin-top:1.2em}.prose a{color:#111827;font-weight:500;text-decoration:underline}.prose strong{color:#111827;font-weight:600}.prose ol{counter-reset:list-counter;margin-bottom:1.25em;margin-top:1.25em}.prose ol>li{counter-increment:list-counter;padding-left:1.75em;position:relative}.prose ol>li:before{color:#6b7280;content:counter(list-counter) ".";font-weight:400;left:0;position:absolute}.prose ul>li{padding-left:1.75em;position:relative}.prose ul>li:before{background-color:#d1d5db;border-radius:50%;content:"";height:.375em;left:.25em;position:absolute;top:.6875em;width:.375em}.prose hr{border-color:#e5e7eb;border-top-width:1px;margin-bottom:3em;margin-top:3em}.prose blockquote{border-left-color:#e5e7eb;border-left-width:.25rem;color:#111827;font-style:italic;font-weight:500;margin-bottom:1.6em;margin-top:1.6em;padding-left:1em;quotes:"\201C""\201D""\2018""\2019"}.prose blockquote p:first-of-type:before{content:open-quote}.prose blockquote p:last-of-type:after{content:close-quote}.prose h1{color:#111827;font-size:2.25em;font-weight:800;line-height:1.1111111;margin-bottom:.8888889em;margin-top:0}.prose h2{color:#111827;font-size:1.5em;font-weight:700;line-height:1.3333333;margin-bottom:1em;margin-top:2em}.prose h3{color:#111827;font-size:1.25em;font-weight:600;line-height:1.6;margin-bottom:.6em;margin-top:1.6em}.prose h4{color:#111827;font-weight:600;line-height:1.5;margin-bottom:.5em;margin-top:1.5em}.prose figure figcaption{color:#6b7280;font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose code{color:#111827;font-size:.875em;font-weight:600}.prose code:after,.prose code:before{content:"`"}.prose a code{color:#111827}.prose pre{background-color:#1f2937;border-radius:.375rem;color:#e5e7eb;font-size:.875em;line-height:1.7142857;margin-bottom:1.7142857em;margin-top:1.7142857em;overflow-x:auto;padding:.8571429em 1.1428571em}.prose pre code{background-color:transparent;border-radius:0;border-width:0;color:inherit;font-family:inherit;font-size:inherit;font-weight:400;line-height:inherit;padding:0}.prose pre code:after,.prose pre code:before{content:""}.prose table{font-size:.875em;line-height:1.7142857;margin-bottom:2em;margin-top:2em;table-layout:auto;text-align:left;width:100%}.prose thead{border-bottom-color:#d1d5db;border-bottom-width:1px;color:#111827;font-weight:600}.prose thead th{padding-bottom:.5714286em;padding-left:.5714286em;padding-right:.5714286em;vertical-align:bottom}.prose tbody tr{border-bottom-color:#e5e7eb;border-bottom-width:1px}.prose tbody tr:last-child{border-bottom-width:0}.prose tbody td{padding:.5714286em;vertical-align:top}.prose{font-size:1rem;line-height:1.75}.prose p{margin-bottom:1.25em;margin-top:1.25em}.prose figure,.prose img,.prose video{margin-bottom:2em;margin-top:2em}.prose figure>*{margin-bottom:0;margin-top:0}.prose h2 code{font-size:.875em}.prose h3 code{font-size:.9em}.prose ul{margin-bottom:1.25em;margin-top:1.25em}.prose li{margin-bottom:.5em;margin-top:.5em}.prose>ul>li p{margin-bottom:.75em;margin-top:.75em}.prose>ul>li>:first-child{margin-top:1.25em}.prose>ul>li>:last-child{margin-bottom:1.25em}.prose>ol>li>:first-child{margin-top:1.25em}.prose>ol>li>:last-child{margin-bottom:1.25em}.prose ol ol,.prose ol ul,.prose ul ol,.prose ul ul{margin-bottom:.75em;margin-top:.75em}.prose h2+*,.prose h3+*,.prose h4+*,.prose hr+*{margin-top:0}.prose thead th:first-child{padding-left:0}.prose thead th:last-child{padding-right:0}.prose tbody td:first-child{padding-left:0}.prose tbody td:last-child{padding-right:0}.prose>:first-child{margin-top:0}.prose>:last-child{margin-bottom:0}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:-webkit-sticky;position:sticky}.inset-0{bottom:0;top:0}.inset-0,.inset-x-0{left:0;right:0}.inset-y-0{bottom:0;top:0}.left-0{left:0}.right-0{right:0}.bottom-0{bottom:0}.top-0{top:0}.top-6{top:1.5rem}.right-6{right:1.5rem}.top-3\.5{top:.875rem}.left-4{left:1rem}.top-3{top:.75rem}.top-5{top:1.25rem}.left-5{left:1.25rem}.-bottom-0\.5{bottom:-.125rem}.-right-1{right:-.25rem}.-bottom-0{bottom:0}.top-4{top:1rem}.z-0{z-index:0}.z-50{z-index:50}.z-40{z-index:40}.z-10{z-index:10}.z-30{z-index:30}.col-span-6{grid-column:span 6/span 6}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-12{grid-column:span 12/span 12}.float-right{float:right}.float-left{float:left}.-m-3{margin:-.75rem}.-m-0\.5{margin:-.125rem}.-m-0{margin:0}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-6{margin-bottom:1.5rem;margin-top:1.5rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.-my-2{margin-bottom:-.5rem;margin-top:-.5rem}.my-5{margin-bottom:1.25rem;margin-top:1.25rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.-my-5{margin-bottom:-1.25rem;margin-top:-1.25rem}.-my-4{margin-bottom:-1rem;margin-top:-1rem}.my-7{margin-bottom:1.75rem;margin-top:1.75rem}.my-3{margin-bottom:.75rem;margin-top:.75rem}.my-10{margin-bottom:2.5rem;margin-top:2.5rem}.my-4{margin-bottom:1rem;margin-top:1rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.mx-px{margin-left:1px;margin-right:1px}.ml-3{margin-left:.75rem}.-ml-px{margin-left:-1px}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.-mr-1{margin-right:-.25rem}.mt-3{margin-top:.75rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-1{margin-top:.25rem}.mb-6{margin-bottom:1.5rem}.mr-2{margin-right:.5rem}.mt-8{margin-top:2rem}.ml-4{margin-left:1rem}.ml-12{margin-left:3rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.-mr-0\.5{margin-right:-.125rem}.-mr-0{margin-right:0}.-mr-2{margin-right:-.5rem}.mr-3{margin-right:.75rem}.mt-10{margin-top:2.5rem}.ml-6{margin-left:1.5rem}.mb-4{margin-bottom:1rem}.-mt-px{margin-top:-1px}.mb-5{margin-bottom:1.25rem}.mb-0{margin-bottom:0}.ml-auto{margin-left:auto}.mr-4{margin-right:1rem}.-mr-12{margin-right:-3rem}.ml-1\.5{margin-left:.375rem}.mb-1{margin-bottom:.25rem}.-mt-1\.5{margin-top:-.375rem}.-mt-1{margin-top:-.25rem}.ml-5{margin-left:1.25rem}.-ml-4{margin-left:-1rem}.ml-8{margin-left:2rem}.mt-12{margin-top:3rem}.-mr-48{margin-right:-12rem}.mt-24{margin-top:6rem}.-ml-48{margin-left:-12rem}.-mb-1{margin-bottom:-.25rem}.mr-1\.5{margin-right:.375rem}.mr-1{margin-right:.25rem}.-mt-4{margin-top:-1rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.-ml-1\.5{margin-left:-.375rem}.-ml-1{margin-left:-.25rem}.mt-0\.5{margin-top:.125rem}.mt-0{margin-top:0}.-mt-0\.5{margin-top:-.125rem}.-mt-0{margin-top:0}.-mb-px{margin-bottom:-1px}.ml-3\.5{margin-left:.875rem}.-mb-8{margin-bottom:-2rem}.ml-7{margin-left:1.75rem}.-ml-0\.5{margin-left:-.125rem}.-ml-0{margin-left:0}.mb-24{margin-bottom:6rem}.-mt-12{margin-top:-3rem}.ml-0{margin-left:0}.-ml-8{margin-left:-2rem}.mr-0\.5{margin-right:.125rem}.mr-0{margin-right:0}.mt-px{margin-top:1px}.ml-2\.5{margin-left:.625rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.h-5{height:1.25rem}.h-16{height:4rem}.h-12{height:3rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-4{height:1rem}.h-9{height:2.25rem}.h-10{height:2.5rem}.h-20{height:5rem}.h-full{height:100%}.h-3{height:.75rem}.h-0{height:0}.h-24{height:6rem}.h-80{height:20rem}.h-32{height:8rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-1\.5{height:.375rem}.h-1{height:.25rem}.h-28{height:7rem}.h-7{height:1.75rem}.h-14{height:3.5rem}.max-h-80{max-height:20rem}.max-h-60{max-height:15rem}.max-h-screen{max-height:100vh}.min-h-screen{min-height:100vh}.min-h-0{min-height:0}.w-5{width:1.25rem}.w-16{width:4rem}.w-full{width:100%}.w-0{width:0}.w-12{width:3rem}.w-6{width:1.5rem}.w-3\/4{width:75%}.w-48{width:12rem}.w-auto{width:auto}.w-8{width:2rem}.w-4{width:1rem}.w-60{width:15rem}.w-10{width:2.5rem}.w-1\/2{width:50%}.w-20{width:5rem}.w-64{width:16rem}.w-32{width:8rem}.w-screen{width:100vw}.w-3{width:.75rem}.w-14{width:3.5rem}.w-24{width:6rem}.w-56{width:14rem}.w-2{width:.5rem}.w-96{width:24rem}.w-1\/3{width:33.333333%}.w-2\.5{width:.625rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-0\.5{width:.125rem}.w-9{width:2.25rem}.w-28{width:7rem}.w-11{width:2.75rem}.w-72{width:18rem}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-screen-xl{max-width:1280px}.max-w-7xl{max-width:80rem}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.max-w-xs{max-width:20rem}.max-w-4xl{max-width:56rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-3xl{max-width:48rem}.max-w-2xl{max-width:42rem}.max-w-5xl{max-width:64rem}.max-w-prose{max-width:65ch}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.flex-none{flex:none}.flex-auto{flex:1 1 auto}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.table-fixed{table-layout:fixed}.origin-top-left{transform-origin:top left}.origin-top{transform-origin:top}.origin-top-right{transform-origin:top right}.translate-y-4{--tw-translate-y:1rem;transform:var(--tw-transform)}.translate-y-0{--tw-translate-y:0px;transform:var(--tw-transform)}.-translate-x-full{--tw-translate-x:-100%;transform:var(--tw-transform)}.translate-x-full{--tw-translate-x:100%;transform:var(--tw-transform)}.translate-x-0{--tw-translate-x:0px;transform:var(--tw-transform)}.translate-y-1{--tw-translate-y:0.25rem;transform:var(--tw-transform)}.translate-y-2{--tw-translate-y:0.5rem;transform:var(--tw-transform)}.translate-x-5{--tw-translate-x:1.25rem;transform:var(--tw-transform)}.rotate-90{--tw-rotate:90deg}.rotate-90,.scale-95{transform:var(--tw-transform)}.scale-95{--tw-scale-x:.95;--tw-scale-y:.95}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-100,.transform{transform:var(--tw-transform)}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}.animate-pulse{-webkit-animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite;animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.content-center{align-content:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-6{gap:1.5rem}.gap-4{gap:1rem}.gap-1{gap:.25rem}.gap-8{gap:2rem}.gap-7{gap:1.75rem}.gap-2{gap:.5rem}.gap-5{gap:1.25rem}.gap-3{gap:.75rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-12{row-gap:3rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-8{row-gap:2rem}.gap-y-4{row-gap:1rem}.gap-y-6{row-gap:1.5rem}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.5rem*var(--tw-space-y-reverse));margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-x-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2.5rem*var(--tw-space-x-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.25rem*var(--tw-space-x-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-y-2>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(2px*var(--tw-divide-y-reverse));border-top-width:calc(2px*(1 - var(--tw-divide-y-reverse)))}.divide-x>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;border-left-width:calc(1px*(1 - var(--tw-divide-x-reverse)));border-right-width:calc(1px*var(--tw-divide-x-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(229,231,235,var(--tw-divide-opacity))}.divide-gray-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(249,250,251,var(--tw-divide-opacity))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(243,244,246,var(--tw-divide-opacity))}.divide-gray-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(107,114,128,var(--tw-divide-opacity))}.divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(209,213,219,var(--tw-divide-opacity))}.divide-gray-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(75,85,99,var(--tw-divide-opacity))}.divide-opacity-10>:not([hidden])~:not([hidden]){--tw-divide-opacity:0.1}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.break-normal{overflow-wrap:normal;word-break:normal}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded-md{border-radius:.375rem}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-xl{border-radius:.75rem}.rounded-sm{border-radius:.125rem}.rounded-none{border-radius:0}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.rounded-b-none{border-bottom-left-radius:0;border-bottom-right-radius:0}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.rounded-b-md{border-bottom-left-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-l-none{border-bottom-left-radius:0;border-top-left-radius:0}.rounded-br-lg{border-bottom-right-radius:.5rem}.rounded-tl{border-top-left-radius:.25rem}.border{border-width:1px}.border-2{border-width:2px}.border-0{border-width:0}.border-b-2{border-bottom-width:2px}.border-l-4{border-left-width:4px}.border-t{border-top-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-r-4{border-right-width:4px}.border-l{border-left-width:1px}.border-b-0{border-bottom-width:0}.border-t-0{border-top-width:0}.border-dashed{border-style:dashed}.border-gray-300{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-indigo-400{--tw-border-opacity:1;border-color:rgba(129,140,248,var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgba(243,244,246,var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgba(248,113,113,var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.border-teal-600{--tw-border-opacity:1;border-color:rgba(0,136,160,var(--tw-border-opacity))}.border-teal-500{--tw-border-opacity:1;border-color:rgba(1,157,187,var(--tw-border-opacity))}.border-pink-500{--tw-border-opacity:1;border-color:rgba(236,72,153,var(--tw-border-opacity))}.border-r-black{--tw-border-opacity:1;border-right-color:rgba(0,0,0,var(--tw-border-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgba(99,102,241,var(--tw-bg-opacity))}.bg-red-700{--tw-bg-opacity:1;background-color:rgba(185,28,28,var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgba(107,114,128,var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgba(79,70,229,var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgba(31,41,55,var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgba(254,226,226,var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgba(238,242,255,var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity:1;background-color:rgba(16,185,129,var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgba(254,243,199,var(--tw-bg-opacity))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgba(251,191,36,var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgba(75,85,99,var(--tw-bg-opacity))}.bg-teal-600{--tw-bg-opacity:1;background-color:rgba(0,136,160,var(--tw-bg-opacity))}.bg-pink-500{--tw-bg-opacity:1;background-color:rgba(236,72,153,var(--tw-bg-opacity))}.bg-purple-500{--tw-bg-opacity:1;background-color:rgba(139,92,246,var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgba(245,158,11,var(--tw-bg-opacity))}.bg-teal-500{--tw-bg-opacity:1;background-color:rgba(1,157,187,var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgba(17,24,39,var(--tw-bg-opacity))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgba(240,249,255,var(--tw-bg-opacity))}.bg-sky-400{--tw-bg-opacity:1;background-color:rgba(56,189,248,var(--tw-bg-opacity))}.bg-sky-500{--tw-bg-opacity:1;background-color:rgba(14,165,233,var(--tw-bg-opacity))}.bg-indigo-900{--tw-bg-opacity:1;background-color:rgba(49,46,129,var(--tw-bg-opacity))}.bg-indigo-800{--tw-bg-opacity:1;background-color:rgba(55,48,163,var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-blue-100{--tw-bg-opacity:1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.bg-rose-500{--tw-bg-opacity:1;background-color:rgba(244,63,94,var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgba(156,163,175,var(--tw-bg-opacity))}.bg-green-200{--tw-bg-opacity:1;background-color:rgba(167,243,208,var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgba(209,250,229,var(--tw-bg-opacity))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgba(224,231,255,var(--tw-bg-opacity))}.bg-green-600{--tw-bg-opacity:1;background-color:rgba(5,150,105,var(--tw-bg-opacity))}.bg-purple-400{--tw-bg-opacity:1;background-color:rgba(167,139,250,var(--tw-bg-opacity))}.bg-opacity-25{--tw-bg-opacity:0.25}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-opacity-10{--tw-bg-opacity:0.1}.bg-opacity-80{--tw-bg-opacity:0.8}.bg-opacity-5{--tw-bg-opacity:0.05}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-tl{background-image:linear-gradient(to top left,var(--tw-gradient-stops))}.from-indigo-600{--tw-gradient-from:#4f46e5;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(79,70,229,0))}.from-indigo-800{--tw-gradient-from:#3730a3;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(55,48,163,0))}.from-gray-900{--tw-gradient-from:#111827;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(17,24,39,0))}.from-indigo-300{--tw-gradient-from:#a5b4fc;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(165,180,252,0))}.from-sky-400{--tw-gradient-from:#38bdf8;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(56,189,248,0))}.to-teal-600{--tw-gradient-to:#0088a0}.to-teal-300,.to-teal-700{--tw-gradient-to:#019dbb}.bg-cover{background-size:cover}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.bg-center{background-position:50%}.bg-no-repeat{background-repeat:no-repeat}.bg-origin-border{background-origin:border-box}.fill-current{fill:currentColor}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-1{padding:.25rem}.p-0\.5{padding:.125rem}.p-0{padding:0}.p-4{padding:1rem}.p-3{padding:.75rem}.p-12{padding:3rem}.p-px{padding:1px}.p-8{padding:2rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.py-8{padding-bottom:2rem;padding-top:2rem}.py-12{padding-bottom:3rem;padding-top:3rem}.py-10{padding-bottom:2.5rem;padding-top:2.5rem}.px-0{padding-left:0;padding-right:0}.px-7{padding-left:1.75rem;padding-right:1.75rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.px-12{padding-left:3rem;padding-right:3rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-16{padding-bottom:4rem;padding-top:4rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-0{padding-bottom:0;padding-top:0}.px-8{padding-left:2rem;padding-right:2rem}.py-2\.5{padding-bottom:.625rem;padding-top:.625rem}.py-14{padding-bottom:3.5rem;padding-top:3.5rem}.py-3\.5{padding-bottom:.875rem;padding-top:.875rem}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.py-px{padding-bottom:1px;padding-top:1px}.pt-6{padding-top:1.5rem}.pt-5{padding-top:1.25rem}.pb-4{padding-bottom:1rem}.pt-1{padding-top:.25rem}.pl-3{padding-left:.75rem}.pr-4{padding-right:1rem}.pt-2{padding-top:.5rem}.pb-3{padding-bottom:.75rem}.pt-4{padding-top:1rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pr-5{padding-right:1.25rem}.pt-8{padding-top:2rem}.pt-0{padding-top:0}.pr-1{padding-right:.25rem}.pb-0{padding-bottom:0}.pl-2{padding-left:.5rem}.pb-5{padding-bottom:1.25rem}.pl-8{padding-left:2rem}.pr-3{padding-right:.75rem}.pb-6{padding-bottom:1.5rem}.pt-16{padding-top:4rem}.pb-32{padding-bottom:8rem}.pl-4{padding-left:1rem}.pt-12{padding-top:3rem}.pb-64{padding-bottom:16rem}.pb-8{padding-bottom:2rem}.pt-3{padding-top:.75rem}.pr-2{padding-right:.5rem}.pl-7{padding-left:1.75rem}.pt-0\.5{padding-top:.125rem}.pl-11{padding-left:2.75rem}.pr-10{padding-right:2.5rem}.pl-1\.5{padding-left:.375rem}.pl-1{padding-left:.25rem}.pl-5{padding-left:1.25rem}.pb-16{padding-bottom:4rem}.pl-10{padding-left:2.5rem}.pr-20{padding-right:5rem}.pb-12{padding-bottom:3rem}.pt-1\.5{padding-top:.375rem}.pl-16{padding-left:4rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-justify{text-align:justify}.font-sans{font-family:Nunito,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-base{font-size:1rem;line-height:1.5rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-5{line-height:1.25rem}.leading-7{line-height:1.75rem}.leading-tight{line-height:1.25}.leading-4{line-height:1rem}.leading-6{line-height:1.5rem}.leading-8{line-height:2rem}.leading-none{line-height:1}.tracking-widest{letter-spacing:.1em}.tracking-wider{letter-spacing:.05em}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.text-gray-500{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgba(75,85,99,var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgba(79,70,229,var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgba(220,38,38,var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgba(17,24,39,var(--tw-text-opacity))}.text-indigo-700{--tw-text-opacity:1;color:rgba(67,56,202,var(--tw-text-opacity))}.text-green-400{--tw-text-opacity:1;color:rgba(52,211,153,var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgba(156,163,175,var(--tw-text-opacity))}.text-indigo-500{--tw-text-opacity:1;color:rgba(99,102,241,var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgba(239,68,68,var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgba(5,150,105,var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgba(16,185,129,var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgba(146,64,14,var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgba(229,231,235,var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgba(209,213,219,var(--tw-text-opacity))}.text-sky-500{--tw-text-opacity:1;color:rgba(14,165,233,var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgba(185,28,28,var(--tw-text-opacity))}.text-teal-200,.text-teal-700{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.text-indigo-200{--tw-text-opacity:1;color:rgba(199,210,254,var(--tw-text-opacity))}.text-transparent{color:transparent}.text-teal-800{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.text-sky-700{--tw-text-opacity:1;color:rgba(3,105,161,var(--tw-text-opacity))}.text-red-400{--tw-text-opacity:1;color:rgba(248,113,113,var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgba(243,244,246,var(--tw-text-opacity))}.text-indigo-300{--tw-text-opacity:1;color:rgba(165,180,252,var(--tw-text-opacity))}.text-teal-600{--tw-text-opacity:1;color:rgba(0,136,160,var(--tw-text-opacity))}.text-green-800{--tw-text-opacity:1;color:rgba(6,95,70,var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity:1;color:rgba(251,191,36,var(--tw-text-opacity))}.text-indigo-800{--tw-text-opacity:1;color:rgba(55,48,163,var(--tw-text-opacity))}.text-indigo-400{--tw-text-opacity:1;color:rgba(129,140,248,var(--tw-text-opacity))}.text-teal-500{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.text-green-200{--tw-text-opacity:1;color:rgba(167,243,208,var(--tw-text-opacity))}.text-opacity-40{--tw-text-opacity:0.4}.text-opacity-100{--tw-text-opacity:1}.underline{text-decoration:underline}.no-underline{text-decoration:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgba(107,114,128,var(--tw-placeholder-opacity))}.placeholder-gray-500:-ms-input-placeholder{--tw-placeholder-opacity:1;color:rgba(107,114,128,var(--tw-placeholder-opacity))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgba(107,114,128,var(--tw-placeholder-opacity))}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-75{opacity:.75}.opacity-50{opacity:.5}.opacity-25{opacity:.25}.opacity-60{opacity:.6}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,0.05)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,0.1),0 1px 2px 0 rgba(0,0,0,0.06)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,0.1),0 2px 4px -1px rgba(0,0,0,0.06)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,0.1),0 10px 10px -5px rgba(0,0,0,0.04)}.shadow-2xl,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,0.25)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-1,.ring-4{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-4{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-8{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-0,.ring-8{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgba(209,213,219,var(--tw-ring-opacity))}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgba(0,0,0,var(--tw-ring-opacity))}.ring-white{--tw-ring-opacity:1;--tw-ring-color:rgba(255,255,255,var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.filter{filter:var(--tw-filter)}.backdrop-blur{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-filter);backdrop-filter:var(--tw-backdrop-filter)}.transition{transition-duration:.15s;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:background-color,border-color,color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.delay-150{transition-delay:.15s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-75{transition-duration:75ms}.duration-300{transition-duration:.3s}.duration-1000{transition-duration:1s}.duration-100{transition-duration:.1s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-linear{transition-timing-function:linear}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-2,.line-clamp-4{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-4{-webkit-line-clamp:4}body.v-tour--active{pointer-events:none}.v-tour{pointer-events:auto}.v-tour__target--highlighted{box-shadow:0 0 0 4px rgba(0,0,0,.4);pointer-events:auto;z-index:9999}.v-tour__target--relative{position:relative}.v-step[data-v-c1b3a65a]{background:#50596c;border-radius:3px;color:#fff;filter:drop-shadow(0 0 2px rgba(0,0,0,.5));max-width:320px;padding:1rem;text-align:center;z-index:10000}.v-step .v-step__arrow[data-v-c1b3a65a]{border-color:#50596c;border-style:solid;height:0;margin:.5rem;position:absolute;width:0}.v-step .v-step__arrow--dark[data-v-c1b3a65a]{border-color:#454d5d}.v-step[x-placement^=top][data-v-c1b3a65a]{margin-bottom:.5rem}.v-step[x-placement^=top] .v-step__arrow[data-v-c1b3a65a]{border-bottom-color:transparent;border-left-color:transparent;border-right-color:transparent;border-width:.5rem .5rem 0;bottom:-.5rem;left:calc(50% - 1rem);margin-bottom:0;margin-top:0}.v-step[x-placement^=bottom][data-v-c1b3a65a]{margin-top:.5rem}.v-step[x-placement^=bottom] .v-step__arrow[data-v-c1b3a65a]{border-left-color:transparent;border-right-color:transparent;border-top-color:transparent;border-width:0 .5rem .5rem;left:calc(50% - 1rem);margin-bottom:0;margin-top:0;top:-.5rem}.v-step[x-placement^=right][data-v-c1b3a65a]{margin-left:.5rem}.v-step[x-placement^=right] .v-step__arrow[data-v-c1b3a65a]{border-bottom-color:transparent;border-left-color:transparent;border-top-color:transparent;border-width:.5rem .5rem .5rem 0;left:-.5rem;margin-left:0;margin-right:0;top:calc(50% - 1rem)}.v-step[x-placement^=left][data-v-c1b3a65a]{margin-right:.5rem}.v-step[x-placement^=left] .v-step__arrow[data-v-c1b3a65a]{border-bottom-color:transparent;border-right-color:transparent;border-top-color:transparent;border-width:.5rem 0 .5rem .5rem;margin-left:0;margin-right:0;right:-.5rem;top:calc(50% - 1rem)}.v-step__header[data-v-c1b3a65a]{background-color:#454d5d;border-top-left-radius:3px;border-top-right-radius:3px;margin:-1rem -1rem .5rem;padding:.5rem}.v-step__content[data-v-c1b3a65a]{margin:0 0 1rem}.v-step__button[data-v-c1b3a65a]{background:0 0;border:.05rem solid #fff;border-radius:.1rem;color:#fff;cursor:pointer;display:inline-block;font-size:.8rem;height:1.8rem;line-height:1rem;margin:0 .2rem;outline:0;padding:.35rem .4rem;text-align:center;text-decoration:none;transition:all .2s ease;vertical-align:middle;white-space:nowrap}.v-step__button[data-v-c1b3a65a]:hover{background-color:rgba(255,255,255,.95);color:#50596c}.mask[data-v-c1b3a65a]{background:rgba(0,0,0,.5);bottom:0;left:0;position:absolute;right:0;top:0}.mask .tour-focus-container[data-v-c1b3a65a]{border-radius:4px;box-shadow:0 0 0 9999px rgba(17,55,80,.4),0 0 15px rgba(0,0,0,.5);height:100%;left:0;opacity:1;pointer-events:auto;position:absolute;top:0;transition:opacity .2s;width:100%}.tooltip{display:inline-block;position:relative}.tooltip .tooltiptext{bottom:100%;left:50%;margin-left:-60px;position:absolute;visibility:hidden;z-index:1}.tooltip:hover .tooltiptext{visibility:visible}.vue-tags-input{max-width:100%!important}.vue-tags-input .ti-new-tag-input{background:transparent}.vue-tags-input .ti-input{border-radius:4px;max-width:none;padding:4px 10px}.vue-tags-input.ti-focus .ti-input{--tw-shadow:0 1px 2px 0 rgba(0,0,0,0.05);border-color:rgba(209,213,219,var(--tw-border-opacity));border-width:1px;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ti-tag[data-v-2fbda277]{border-radius:4px!important;font-size:1em!important}.ti-new-tag-input-wrapper input:focus{border-color:transparent;box-shadow:none!important;outline:none!important;outline:0 solid transparent;outline-offset:0}.index_beams{background-color:#fff;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600'%3E%3Cpath fill='%23009ebb' fill-opacity='.08' d='M600 325.1v-1.17c-6.5 3.83-13.06 7.64-14.68 8.64-10.6 6.56-18.57 12.56-24.68 19.09-5.58 5.95-12.44 10.06-22.42 14.15-1.45.6-2.96 1.2-4.83 1.9l-4.75 1.82c-9.78 3.75-14.8 6.27-18.98 10.1-4.23 3.88-9.65 6.6-16.77 8.84-1.95.6-3.99 1.17-6.47 1.8l-6.14 1.53c-5.29 1.35-8.3 2.37-10.54 3.78-3.08 1.92-6.63 3.26-12.74 5.03a384.1 384.1 0 0 1-4.82 1.36c-2.04.58-3.6 1.04-5.17 1.52a110.03 110.03 0 0 0-11.2 4.05c-2.7 1.15-5.5 3.93-8.78 8.4a157.68 157.68 0 0 0-6.15 9.2c-5.75 9.07-7.58 11.74-10.24 14.51a50.97 50.97 0 0 1-4.6 4.22c-2.33 1.9-10.39 7.54-11.81 8.74a14.68 14.68 0 0 0-3.67 4.15c-1.24 2.3-1.9 4.57-2.78 8.87-2.17 10.61-3.52 14.81-8.2 22.1-4.07 6.33-6.8 9.88-9.83 12.99-.47.48-.95.96-1.5 1.48l-3.75 3.56c-1.67 1.6-3.18 3.12-4.86 4.9a42.44 42.44 0 0 0-9.89 16.94c-2.5 8.13-2.72 15.47-1.76 27.22.47 5.82.51 6.36.51 8.18 0 10.51.12 17.53.63 25.78.24 4.05.56 7.8.97 11.22h.9c-1.13-9.58-1.5-21.83-1.5-37 0-1.86-.04-2.4-.52-8.26-.94-11.63-.72-18.87 1.73-26.85a41.44 41.44 0 0 1 9.65-16.55c1.67-1.76 3.18-3.27 4.83-4.85.63-.6 3.13-2.96 3.75-3.57a71.6 71.6 0 0 0 1.52-1.5c3.09-3.16 5.86-6.76 9.96-13.15 4.77-7.42 6.15-11.71 8.34-22.44.86-4.21 1.5-6.4 2.68-8.6.68-1.25 1.79-2.48 3.43-3.86 1.38-1.15 9.43-6.8 11.8-8.72 1.71-1.4 3.26-2.81 4.7-4.3 2.72-2.85 4.56-5.54 10.36-14.67a156.9 156.9 0 0 1 6.1-9.15c3.2-4.33 5.9-7.01 8.37-8.07 3.5-1.5 7.06-2.77 11.1-4.02a233.84 233.84 0 0 1 7.6-2.2l2.38-.67c6.19-1.79 9.81-3.16 12.98-5.15 2.14-1.33 5.08-2.33 10.27-3.65l6.14-1.53c2.5-.63 4.55-1.2 6.52-1.82 7.24-2.27 12.79-5.06 17.15-9.05 4.05-3.72 9-6.2 18.66-9.9l4.75-1.82c1.87-.72 3.39-1.31 4.85-1.91 10.1-4.15 17.07-8.32 22.76-14.4 6.05-6.45 13.95-12.4 24.49-18.92 1.56-.96 7.82-4.6 14.15-8.33v-64.58c-4 8.15-8.52 14.85-12.7 17.9-2.51 1.82-5.38 4.02-9.04 6.92a1063.87 1063.87 0 0 0-6.23 4.98l-1.27 1.02a2309.25 2309.25 0 0 1-4.87 3.9c-7.55 6-12.9 10.05-17.61 13.19-3.1 2.06-3.86 2.78-8.06 7.13-5.84 6.07-11.72 8.62-29.15 10.95-11.3 1.5-20.04 4.91-30.75 11.07-1.65.94-7.27 4.27-6.97 4.1-2.7 1.58-4.69 2.69-6.64 3.66-5.63 2.8-10.47 4.17-15.71 4.17-17.13 0-41.44 11.51-51.63 22.83-12.05 13.4-31.42 27.7-45.25 31.16-7.4 1.85-11.85 7.05-14.04 14.69-1.26 4.4-1.58 8.28-1.58 13.82 0 .82.01.98.24 3.63.45 5.18.35 8.72-.77 13.26-1.53 6.2-4.89 12.6-10.59 19.43-13.87 16.65-22.88 46.58-22.88 71.68 0 2.39.02 4.26.06 8.75.12 10.8.1 15.8-.22 21.95-.56 11.18-2.09 20.73-5 29.3h-1.05c2.94-8.56 4.49-18.12 5.05-29.35.31-6.13.34-11.1.22-21.9-.04-4.48-.06-6.36-.06-8.75 0-25.32 9.07-55.47 23.12-72.32 5.6-6.72 8.88-12.99 10.38-19.03 1.09-4.4 1.18-7.85.74-12.93-.23-2.7-.24-2.86-.24-3.72 0-5.62.32-9.57 1.62-14.1 2.28-7.95 6.97-13.44 14.76-15.39 13.6-3.4 32.82-17.59 44.75-30.84C409 360.14 433.58 348.5 451 348.5c5.07 0 9.77-1.33 15.26-4.07 1.93-.96 3.9-2.05 6.58-3.62-.3.18 5.33-3.16 6.98-4.11 10.82-6.21 19.66-9.67 31.11-11.2 17.23-2.3 22.9-4.75 28.57-10.64 4.25-4.41 5.04-5.16 8.22-7.28 4.68-3.11 10.01-7.14 17.55-13.14a1113.33 1113.33 0 0 0 4.86-3.89l1.28-1.02a4668.54 4668.54 0 0 1 6.23-4.98c3.67-2.9 6.55-5.12 9.07-6.95 4.37-3.19 9.16-10.56 13.29-19.4v66.9zm0-116.23c-.62.01-1.27.06-1.95.13-6.13.63-13.83 3.45-21.83 7.45-3.64 1.82-8.46 2.67-14.17 2.71-4.7.04-9.72-.47-14.73-1.33-1.7-.3-3.26-.61-4.67-.93a31.55 31.55 0 0 0-3.55-.57 273.4 273.4 0 0 0-16.66-.88c-10.42-.16-17.2.74-17.97 2.73-.38.97.6 2.55 3.03 4.87 1.01.97 2.22 2.03 4.04 3.55a1746.07 1746.07 0 0 0 4.79 4.02c1.39 1.2 3.1 1.92 5.5 2.5.7.16.86.2 2.64.54 3.53.7 5.03 1.25 6.15 2.63 1.41 1.76 1.4 4.54-.15 8.88-2.44 6.83-5.72 10.05-10.19 10.33-3.63.23-7.6-1.29-14.52-5.06-4.53-2.47-6.82-7.3-8.32-15.26-.17-.87-.32-1.78-.5-2.86l-.43-2.76c-1.05-6.58-1.9-9.2-3.73-10.11-.81-.4-1.59-.74-2.36-1-2.27-.77-4.6-1.02-8.1-.92-2.29.07-14.7 1-13.77.93-20.55 1.37-28.8 5.05-37.09 14.99a133.07 133.07 0 0 0-4.25 5.44l-2.3 3.09-2.51 3.32c-4.1 5.36-7.06 8.48-10.39 11.12-.65.52-1.33 1.04-2.13 1.62l-4.11 2.94a106.8 106.8 0 0 0-5.16 3.99c-4.55 3.74-9.74 8.6-16.25 15.38-8.25 8.58-11.78 13.54-11.7 15.95.07 1.65 1.64 2.11 6.79 2.38 1.61.09 2.15.12 2.98.2 2.95.24 5.09.73 6.81 1.68 7.48 4.15 11.63 7.26 13.95 11.58 3.3 6.15.8 12.88-8.89 20.26-8.28 6.3-11.1 10.37-11.31 14.96-.06 1.17 0 1.93.26 4.43.69 6.47.25 10.65-2.8 17.42a44.23 44.23 0 0 1-4.16 7.53c-2.82 3.97-5.47 5.74-10.6 7.69-.43.16-3.34 1.23-4.27 1.59-1.8.68-3.38 1.36-5.01 2.14-4.18 2-8.4 4.6-13.1 8.24-8.44 6.51-13.23 14.56-15.98 25.06-1.1 4.2-1.55 6.81-2.8 15.21-1.26 8.6-2.17 12.64-4.08 16.55-2.1 4.28-11.93 26.59-12.97 28.88a382.7 382.7 0 0 1-6.37 13.41c-4.07 8.11-7.61 14.07-10.73 17.81-5.38 6.46-8.98 14.37-13.77 28.42a810.14 810.14 0 0 0-1.89 5.6c-1.8 5.35-2.96 8.6-4.26 11.85-6.13 15.32-25.43 26.31-46.46 26.31-11.2 0-20.58-2.74-31.02-8.55-5.6-3.13-4.55-2.42-22.26-14.54-14.33-9.8-17.7-10.73-20.47-6.9-.37.5-1.81 2.74-1.83 2.77a52.24 52.24 0 0 1-4.94 5.9c-.73.79-5.52 5.87-6.97 7.45-2.38 2.6-4.3 4.81-5.98 6.93a45.6 45.6 0 0 0-5.08 7.66c-1.29 2.57-1.9 5.25-2.66 10.6a997.6 997.6 0 0 1-.46 3.18h-1l.47-3.32c.77-5.45 1.4-8.2 2.75-10.9a46.54 46.54 0 0 1 5.2-7.84c1.7-2.14 3.63-4.38 6.03-6.98 1.45-1.59 6.24-6.68 6.96-7.46a51.58 51.58 0 0 0 4.84-5.78s1.47-2.26 1.86-2.8c3.25-4.5 7.08-3.44 21.84 6.67 17.67 12.08 16.62 11.38 22.19 14.48 10.3 5.73 19.5 8.43 30.53 8.43 20.65 0 39.57-10.77 45.54-25.69a219.7 219.7 0 0 0 4.24-11.8 6752.32 6752.32 0 0 0 1.88-5.6c4.83-14.16 8.47-22.14 13.96-28.73 3.05-3.66 6.56-9.57 10.6-17.61 1.97-3.93 4.04-8.31 6.35-13.38 1.03-2.28 10.88-24.61 12.98-28.91 1.85-3.79 2.75-7.76 4-16.25 1.24-8.44 1.7-11.07 2.81-15.32 2.8-10.7 7.71-18.94 16.33-25.6a73.18 73.18 0 0 1 13.29-8.35c1.66-.8 3.27-1.48 5.08-2.18.94-.36 3.86-1.43 4.28-1.59 4.95-1.88 7.44-3.55 10.14-7.33 1.35-1.9 2.68-4.3 4.06-7.37 2.97-6.58 3.39-10.59 2.72-16.9a27.13 27.13 0 0 1-.27-4.58c.22-4.94 3.21-9.24 11.7-15.7 9.33-7.11 11.66-13.34 8.62-19-2.2-4.09-6.25-7.12-13.55-11.17-1.57-.88-3.6-1.33-6.42-1.57-.8-.07-1.34-.1-2.95-.19-5.77-.3-7.63-.85-7.72-3.34-.1-2.81 3.5-7.87 11.97-16.69 6.53-6.8 11.75-11.69 16.33-15.45 1.79-1.47 3.42-2.72 5.2-4.03l4.12-2.94c.79-.58 1.46-1.08 2.1-1.59 3.26-2.6 6.16-5.65 10.21-10.94a383.2 383.2 0 0 0 2.5-3.32l2.31-3.09c1.8-2.39 3.04-4 4.29-5.48 8.47-10.17 16.98-13.96 37.27-15.3-.44.02 12-.9 14.32-.98 3.62-.1 6.05.16 8.46.98.8.27 1.62.62 2.47 1.04 2.27 1.14 3.17 3.87 4.27 10.85l.44 2.76c.17 1.07.33 1.97.5 2.83 1.44 7.69 3.62 12.29 7.8 14.57 6.76 3.68 10.6 5.15 13.99 4.94 4-.25 6.99-3.17 9.3-9.67 1.45-4.04 1.46-6.49.32-7.92-.9-1.12-2.28-1.62-5.57-2.27a55.8 55.8 0 0 1-2.67-.55c-2.54-.6-4.39-1.4-5.93-2.71a252.63 252.63 0 0 0-4.78-4.01 84.35 84.35 0 0 1-4.08-3.6c-2.73-2.6-3.86-4.43-3.28-5.95 1.02-2.64 7.82-3.54 18.93-3.37a230.56 230.56 0 0 1 16.73.88c2.76.39 3.2.49 3.68.6 1.4.3 2.95.62 4.62.91a82.9 82.9 0 0 0 14.56 1.32c5.56-.04 10.24-.86 13.73-2.6 8.1-4.05 15.89-6.9 22.17-7.56.7-.07 1.4-.11 2.05-.13v1zm0-100.94v1.5c-8.62 16.05-17.27 29.55-23.65 35.92-3.19 3.2-7.62 4.9-13.54 5.56-4.45.48-8.28.4-19.18-.2-9.91-.55-15.32-.44-20.52.78a84.05 84.05 0 0 1-15 2.11l-2.25.14c-12.49.75-19.37 1.78-32.72 5.74-4.5 1.33-9.27 2.49-14.3 3.48a246.27 246.27 0 0 1-32.6 3.97c-7.56.45-13.21.57-20.24.57-5.4 0-11.9 1.61-18 5.18-8.3 4.87-15.06 12.87-19.53 24.5a68.57 68.57 0 0 1-4.56 9.8c-3.6 6.2-6.92 8.99-13.38 12.18l-4.03 1.96a64.48 64.48 0 0 0-15.16 10.25c-8.2 7.33-13.72 16.63-22.54 35.6l-2.08 4.49c-7.3 15.7-11.5 23.3-17.35 29.87-7.7 8.66-20.25 14.42-40.31 20.08-4.37 1.23-19.04 5.08-19.24 5.13-6.92 1.87-11.68 3.34-15.63 4.92-10.55 4.22-18.71 10.52-36.38 26.52l-1.7 1.54c-8.58 7.76-13.41 11.9-18.81 15.88-3.95 2.9-8 5.67-12.97 8.91-2.06 1.34-10.3 6.6-12.33 7.94-11.52 7.5-18.53 13.04-24.62 20.08a62.01 62.01 0 0 0-6.44 8.85c-4.13 6.91-6.27 13.15-9.2 25.11l-1.54 6.26c-.6 2.45-1.15 4.54-1.72 6.58-2.97 10.7-6.9 17.36-14.78 26.91L69.6 491a148.51 148.51 0 0 0-4.19 5.3 23.9 23.9 0 0 0-3.44 6.28c-1.16 3.23-1.52 5.9-1.87 11.94-.58 10.05-1.42 15.04-4.63 22.67-1.57 3.72-5.66 14.02-6.41 15.8a73.46 73.46 0 0 1-3.57 7.4c-2.88 5.14-6.71 10.12-13.12 16.95-5.96 6.36-8.87 10.9-10.61 16a56.88 56.88 0 0 0-1.38 4.82l-.46 1.84h-1.03l.52-2.08c.52-2.09.92-3.49 1.4-4.9 1.8-5.25 4.78-9.9 10.84-16.36 6.35-6.78 10.13-11.7 12.97-16.77a72.5 72.5 0 0 0 3.52-7.29c.75-1.76 4.84-12.06 6.4-15.8 3.17-7.5 3.99-12.4 4.56-22.33.35-6.14.72-8.88 1.93-12.23a24.9 24.9 0 0 1 3.58-6.54c1.27-1.7 2.6-3.37 4.22-5.34l4.11-4.95c7.8-9.46 11.66-16 14.59-26.54.56-2.04 1.1-4.12 1.71-6.56l1.53-6.26c2.96-12.04 5.13-18.36 9.32-25.39 1.84-3.08 4-6.05 6.54-8.99 6.17-7.12 13.24-12.7 24.83-20.26 2.05-1.33 10.28-6.6 12.33-7.94 4.96-3.22 9-5.98 12.92-8.87 5.37-3.95 10.19-8.08 18.74-15.82l1.7-1.54c17.76-16.09 25.98-22.43 36.67-26.7 4-1.6 8.8-3.09 15.75-4.96.21-.06 14.87-3.9 19.22-5.13 19.9-5.61 32.32-11.31 39.85-19.78 5.76-6.48 9.93-14.02 17.18-29.64l2.09-4.5c8.87-19.07 14.44-28.46 22.77-35.9a65.48 65.48 0 0 1 15.38-10.4l4.04-1.97c6.3-3.1 9.47-5.77 12.96-11.77a67.6 67.6 0 0 0 4.48-9.67c4.56-11.84 11.47-20.02 19.97-25 6.25-3.66 12.93-5.32 18.5-5.32 7.01 0 12.65-.12 20.17-.57a245.3 245.3 0 0 0 32.47-3.96c5-.98 9.75-2.13 14.22-3.45 13.43-3.98 20.38-5.02 32.94-5.78l2.24-.14c5.76-.37 9.8-.9 14.85-2.09 5.31-1.25 10.79-1.35 22.6-.7 9.04.5 12.84.58 17.21.1 5.71-.62 9.94-2.26 12.95-5.26 6.44-6.45 15.3-20.37 24.35-36.72zm0 450.21c-1.28-4.6-2.2-10.55-3.33-20.25l-.24-2.04-.23-2.03c-1.82-15.7-3.07-21.98-5.55-24.47-2.46-2.46-3.04-5.03-2.52-8.64.1-.6.18-1.1.39-2.15.69-3.54.77-5.04.08-6.84-.91-2.38-3.31-4.41-7.79-6.26-5.08-2.09-6.52-4.84-4.89-8.44.66-1.45 1.79-3.02 3.52-5.01 1.04-1.2 5.48-5.96 5.08-5.53 6.15-6.7 8.98-11.34 8.98-16.48a15.2 15.2 0 0 1 6.5-12.89v1.26a14.17 14.17 0 0 0-5.5 11.63c0 5.47-2.93 10.29-9.24 17.16.38-.42-4.04 4.33-5.07 5.5-1.67 1.93-2.75 3.43-3.36 4.77-1.37 3.04-.23 5.22 4.36 7.1 4.71 1.95 7.32 4.16 8.34 6.83.78 2.04.7 3.67-.03 7.4-.2 1.03-.3 1.51-.38 2.09-.48 3.33.03 5.59 2.23 7.8 2.74 2.74 3.98 8.96 5.84 25.06l.24 2.03.23 2.04c.82 7.01 1.53 12.06 2.34 16.03v4.33zm0-62.16c-1.4-3.13-4.43-9.9-4.95-11.17-1.02-2.53-1.25-3.8-.91-5.18.2-.84 2.05-4.68 2.32-5.33a70.79 70.79 0 0 0 3.54-11.2v3.99a62.82 62.82 0 0 1-2.62 7.6c-.31.75-2.09 4.46-2.27 5.18-.28 1.12-.08 2.22.87 4.57.41 1.02 2.5 5.7 4.02 9.09v2.45zm0-85.09c-1.65 1.66-3.66 2.9-6.4 4.13-.25.1-13.97 5.47-20.4 8.43-9.35 4.32-16.7 5.9-23.03 5.25-5.08-.53-9.02-2.25-14.77-5.92l-3.2-2.07a77.4 77.4 0 0 0-5.44-3.27c-4.05-2.18-3.25-5.8 1.47-10.47 3.71-3.68 9.6-7.93 18.73-13.8l4.46-2.82c17.95-11.33 18.22-11.5 22.27-14.74 11.25-9 19.69-14.02 26.31-15.1v1.02c-6.37 1.1-14.62 6-25.69 14.86-4.1 3.28-4.34 3.44-22.36 14.8a652.4 652.4 0 0 0-4.45 2.83c-9.07 5.83-14.92 10.05-18.57 13.66-4.31 4.28-4.95 7.13-1.7 8.88 1.7.91 3.29 1.88 5.5 3.3l3.2 2.08c5.64 3.59 9.45 5.25 14.34 5.76 6.13.64 13.32-.9 22.52-5.15 6.46-2.98 20.18-8.35 20.4-8.44 3.04-1.37 5.1-2.71 6.81-4.69v1.47zm0-41.37v1c-6.56.26-12.11 3.13-19.71 9.08l-4.63 3.68a51.87 51.87 0 0 1-4.4 3.14c-.82.52-5.51 3.33-6.22 3.76-3.31 2-6.15 3.8-8.87 5.6a112.61 112.61 0 0 0-8.16 5.92c-4.61 3.72-7.4 6.9-7.97 9.35-.63 2.67 1.48 4.53 7.05 5.46 10.7 1.78 20.92-.05 30.45-4.65a61.96 61.96 0 0 0 17.1-12.2 41.8 41.8 0 0 0 5.36-7.42v1.92a38.94 38.94 0 0 1-4.64 6.19 62.95 62.95 0 0 1-17.39 12.41c-9.7 4.68-20.13 6.55-31.05 4.73-6.06-1-8.65-3.29-7.85-6.67.64-2.74 3.53-6.05 8.31-9.9 2.35-1.9 5.1-3.88 8.24-5.97 2.73-1.82 5.58-3.61 8.9-5.62.72-.44 5.4-3.24 6.22-3.75 1.26-.8 2.6-1.76 4.3-3.09.8-.62 3.9-3.1 4.63-3.67 7.77-6.1 13.49-9.04 20.33-9.3zm0-154.6v1c-1.75-.24-4.3.23-7.82 1.55-10.01 3.75-13.8 5.07-19.15 6.76-1.78.56-2.63.83-3.87 1.24-1.48.5-3.16.76-6.74 1.16a1550.34 1550.34 0 0 0-2.64.3c-7.8.94-11.28 2.47-11.28 6.07 0 4.45 2.89 13.18 7.96 25.81a57.34 57.34 0 0 1 2.33 7.6 258.32 258.32 0 0 1 .84 3.46c1.86 7.62 3.17 10.71 5.56 11.67 2.21.88 4.7.6 7.47-.72 3.48-1.69 7.22-4.94 11.2-9.47 1.52-1.7 2.97-3.49 4.59-5.57l3.16-4.1c2.59-3.23 6.07-12.21 8.39-20.23v3.45c-2.29 7.2-5.27 14.5-7.61 17.41-.44.55-2.67 3.46-3.15 4.09-1.63 2.1-3.1 3.9-4.62 5.62-4.08 4.61-7.9 7.94-11.53 9.7-2.99 1.44-5.77 1.75-8.28.74-2.84-1.13-4.2-4.34-6.15-12.35a2097.48 2097.48 0 0 1-.84-3.46c-.8-3.2-1.47-5.45-2.28-7.46-5.14-12.8-8.04-21.55-8.04-26.19 0-4.37 3.84-6.06 12.16-7.07a160.9 160.9 0 0 1 2.65-.3c3.5-.39 5.15-.64 6.53-1.1 1.26-.42 2.1-.7 3.88-1.26 5.34-1.68 9.11-3 19.1-6.74 3.53-1.32 6.22-1.84 8.18-1.61zM0 292c10.13-11.31 18.13-23.2 23.07-35.39 3.3-8.14 6.09-16.12 10.81-30.55l1.59-4.84c6.53-19.94 10.11-29.82 14.77-39.56 6.07-12.72 12.55-21.18 20.27-25.54 6.66-3.76 10.2-7.86 12.22-13.15a46.6 46.6 0 0 0 1.86-6.58c1.23-5.2 2.05-7.59 3.93-10.36 2.45-3.62 6.27-6.53 12.1-8.96 15.78-6.58 16.73-7.04 18.05-9.01.65-.98.83-2.15.74-4.51-.03-.73-.23-3.82-.24-4A93.8 93.8 0 0 1 119 94c0-10.04.18-11.37 2.37-13.15.52-.42 1.13-.8 2.07-1.3.27-.14 2.18-1.12 2.84-1.48a68.4 68.4 0 0 0 9.12-5.87c2.06-1.54 2.64-2.14 8.01-7.93 3.78-4.09 6.21-6.36 8.96-8.12 3.64-2.33 7.2-3.12 10.9-2.11 4.4 1.2 10.81 2 18.78 2.46 6.9.4 12.9.5 21.95.5 4.87 0 8.97.47 15.4 1.57 7.77 1.33 9.3 1.54 12.38 1.54 4.05 0 7.43-.88 10.68-2.95 5.06-3.22 8.11-4.67 11.2-5.2 3.62-.64 4.77-.46 16.55 2.06 17.26 3.7 30.85 1.36 41.06-9.7 5.1-5.53 5.48-8.9 3.48-14.8-.83-2.42-1.03-3.1-1.17-4.3-.29-2.52.5-4.71 2.71-6.93 2.65-2.65 4.72-9.17 6.22-18.29h2.03c-1.56 9.71-3.77 16.65-6.83 19.7-1.79 1.8-2.36 3.39-2.14 5.28.11 1 .3 1.63 1.07 3.9 2.22 6.53 1.76 10.66-3.9 16.8-10.77 11.66-25.07 14.13-42.95 10.3-11.42-2.45-12.55-2.62-15.78-2.06-2.77.48-5.62 1.84-10.47 4.92a20.93 20.93 0 0 1-11.76 3.27c-3.25 0-4.81-.22-12.73-1.57C212.74 59.46 208.73 59 204 59c-9.1 0-15.11-.1-22.07-.5-8.09-.47-14.62-1.29-19.2-2.54-5.62-1.53-10.17 1.38-17.85 9.66-5.5 5.94-6.08 6.53-8.28 8.18a70.38 70.38 0 0 1-9.38 6.03c-.68.37-2.58 1.35-2.84 1.49-.84.44-1.35.76-1.75 1.08-1.47 1.2-1.63 2.4-1.63 11.6 0 1.85.06 3.54.17 5.44 0 .17.2 3.28.24 4.03.1 2.75-.13 4.29-1.08 5.71-1.67 2.5-2.27 2.8-18.95 9.74-5.48 2.29-8.99 4.96-11.2 8.24-1.71 2.51-2.47 4.73-3.64 9.7-.83 3.5-1.21 4.92-1.94 6.83-2.18 5.73-6.05 10.19-13.1 14.18-7.3 4.12-13.55 12.28-19.46 24.66-4.6 9.64-8.17 19.46-14.67 39.32l-1.58 4.84c-4.75 14.47-7.54 22.48-10.86 30.69-5.28 13.01-13.95 25.65-24.93 37.6v-2.97zm0 78v-.5l1-.01c6.32 0 7.47 5.2 4.6 13.36a60.36 60.36 0 0 1-5.6 11.3v-1.92a57.76 57.76 0 0 0 4.65-9.72c2.69-7.6 1.71-12.02-3.65-12.02-.34 0-.67 0-1 .02v-46.59a340.96 340.96 0 0 0 13.71-8.34c13.66-9.46 29.79-37.6 29.79-53.59 0-18.1 21.57-72.64 32.23-79.42 12.71-8.09 32.24-27.96 35.8-37.75 1.93-5.3 5.5-7.27 14.42-9.37 6.15-1.44 8.64-2.42 10.67-4.79 1.5-1.74 2.72-4.79 4.33-10.3.23-.78 1.9-6.68 2.43-8.46 3.62-12.08 7.3-18.49 13.47-20.39 2.5-.76 3.03-.98 9.74-3.7 7.49-3.03 11.97-4.43 17.12-4.92 6.75-.65 13.13.75 19.55 4.67 5.43 3.32 12.19 4.72 20.17 4.56 6.03-.12 12.2-1.07 19.83-2.8 1.82-.4 7.38-1.74 8.26-1.94 2.69-.6 4.34-.89 5.48-.89 4.97 0 8.93-.05 14.2-.27 7.9-.32 15.56-.92 22.75-1.88 8.5-1.14 15.9-2.73 21.88-4.82 18.9-6.62 32.64-18.3 33.67-27.59.29-2.56.4-2.96 2.79-11.11 2.33-7.95 3.21-12.93 2.72-18.23-.2-2.24-.69-4.38-1.48-6.42-1.5-3.92-2.63-9.4-3.43-16.18h.9c.77 6.47 1.89 11.72 3.47 15.82a24.93 24.93 0 0 1 1.54 6.69c.5 5.46-.4 10.54-2.77 18.6-2.36 8.06-2.47 8.47-2.74 10.95-1.09 9.75-15.1 21.68-34.33 28.41-6.06 2.12-13.52 3.72-22.09 4.87-7.22.96-14.92 1.57-22.83 1.89-5.3.21-9.27.27-14.25.27-1.04 0-2.64.27-5.26.87-.87.2-6.43 1.53-8.26 1.94-7.68 1.73-13.92 2.7-20.03 2.82-8.15.17-15.1-1.27-20.71-4.7-6.23-3.81-12.4-5.16-18.93-4.54-5.04.48-9.44 1.86-16.84 4.86-6.75 2.74-7.29 2.95-9.82 3.73-5.73 1.76-9.28 7.96-12.81 19.72-.53 1.77-2.2 7.66-2.43 8.46-1.66 5.65-2.91 8.78-4.53 10.67-2.22 2.58-4.84 3.62-12.01 5.3-7.8 1.83-11.13 3.66-12.9 8.54-3.65 10.04-23.32 30.06-36.2 38.25C65.94 190 44.5 244.2 44.5 262c0 16.34-16.3 44.78-30.22 54.41-2.14 1.48-8.24 5.12-14.28 8.68v-1.16 46.09zm0-173.7v-1.11c7.42-3.82 14.55-10.23 21.84-18.98 3.8-4.56 14.21-18.78 15.79-20.55 1.8-2.04 4.06-3.96 7.42-6.45 1.08-.8 4.92-3.57 5.49-3.99 9.36-6.85 14-11.96 15.98-19.36.8-2.98 1.54-6.78 2.46-12.3.23-1.44 2-12.46 2.56-15.79 2.87-16.77 5.73-26.79 10.07-32.1C92.46 52.43 101.5 38.13 101.5 33c0-2.54.34-3.35 6.05-15.71.68-1.49 1.25-2.74 1.77-3.93 2.5-5.75 3.9-10.04 4.14-13.36h1c-.23 3.48-1.66 7.87-4.23 13.76-.52 1.2-1.09 2.45-1.78 3.95-5.54 12.01-5.95 12.99-5.95 15.29 0 5.47-9.09 19.84-20.11 33.31-4.2 5.12-7.03 15.06-9.86 31.64-.57 3.33-2.33 14.33-2.57 15.78-.92 5.56-1.67 9.38-2.48 12.4-2.05 7.68-6.82 12.93-16.35 19.91l-5.49 3.98c-3.3 2.45-5.51 4.34-7.27 6.31-1.53 1.73-11.94 15.93-15.76 20.53-7.52 9.02-14.88 15.6-22.61 19.46zm0 361.83v-4.33c.48 2.36 1 4.35 1.6 6.15 2 6.03 4.6 8.26 8.19 6.59C28.76 557.69 43.5 542.4 43.5 527c0-16.2 6.37-31.99 17.1-46.3 1.88-2.5 3.66-4.4 5.53-6 .73-.62 1.45-1.18 2.3-1.8l2-1.43c3.68-2.68 5.32-5.28 7.08-12.59.75-3.07 1.38-5.02 4.2-13.26l.63-1.88c3.24-9.58 4.56-14.97 4.17-18.65-.48-4.43-3.8-5.23-11.3-1.64a81.12 81.12 0 0 1-9.15 3.7c-13.89 4.67-26.96 5.8-42.66 5.42l-1.95-.05-1.45-.02a39.8 39.8 0 0 0-15.05 2.96A21.81 21.81 0 0 0 0 438.37v-1.26a23.55 23.55 0 0 1 4.55-2.57 40.77 40.77 0 0 1 16.92-3.02l1.95.05c15.6.38 28.57-.75 42.32-5.37a80.12 80.12 0 0 0 9.04-3.65c8.04-3.84 12.16-2.85 12.72 2.43.42 3.89-.92 9.34-4.21 19.08l-.64 1.88c-2.8 8.2-3.43 10.15-4.16 13.18-1.82 7.52-3.59 10.34-7.47 13.16l-2 1.43c-.84.6-1.54 1.15-2.25 1.75a35.45 35.45 0 0 0-5.37 5.84c-10.61 14.15-16.9 29.74-16.9 45.7 0 15.88-15 31.45-34.29 40.45-4.3 2.01-7.39-.66-9.56-7.18-.23-.68-.44-1.39-.65-2.13zm0-62.16v-2.45l1.46 3.27c2.1 4.8 3.46 10.33 4.26 16.77.66 5.3.84 9.3 1.04 18.5.2 9.32.5 12.75 1.63 15.05 1.28 2.6 3.67 2.35 8.29-1.5 17.14-14.3 21.82-22.9 21.82-38.62 0-7.17 1.1-12.39 3.7-17.68 2.27-4.67 3.65-6.62 13.4-19.62a69.8 69.8 0 0 1 7.6-8.79 44.76 44.76 0 0 1 3.54-3.06c.38-.3.64-.52.89-.74a10.47 10.47 0 0 0 2.63-3.32 35.78 35.78 0 0 0 2.26-5.94l.37-1.2.36-1.15c.29-.91.48-1.55.66-2.16.45-1.53.74-2.68.91-3.66.38-2.2.12-3.49-.85-4.15-2.35-1.61-9.28-.24-23.8 4.94-9.54 3.4-16.12 4.17-27.85 4.26-7.71.06-10.43.4-13.25 2.12-3.48 2.12-5.84 6.4-7.58 14.26-.5 2.2-.99 4.19-1.49 5.98v-3.98l.51-2.22c1.8-8.1 4.28-12.6 8.04-14.9 3.04-1.85 5.86-2.2 13.77-2.26 11.61-.09 18.1-.84 27.51-4.2 14.93-5.32 21.95-6.71 24.7-4.83 1.38.94 1.71 2.6 1.28 5.15a33.69 33.69 0 0 1-.94 3.78l-.66 2.17-.36 1.15-.37 1.2a36.64 36.64 0 0 1-2.33 6.1c-.8 1.53-1.61 2.52-2.86 3.61l-.92.77-1.02.83c-.9.74-1.65 1.4-2.47 2.18a68.84 68.84 0 0 0-7.48 8.66c-9.7 12.93-11.07 14.87-13.31 19.46-2.52 5.15-3.59 10.22-3.59 17.24 0 16.04-4.82 24.91-22.18 39.38-5.04 4.2-8.18 4.55-9.83 1.18-1.22-2.5-1.52-5.94-1.73-15.47-.2-9.16-.38-13.15-1.03-18.4-.79-6.34-2.12-11.8-4.19-16.49L0 495.98zM379.27 0h1.04l1.5 5.26c3.28 11.56 4.89 19.33 5.26 27.8.49 11.01-1.52 21.26-6.63 31.17-7.8 15.13-20.47 26.5-36.22 34.1-12.38 5.96-26.12 9.17-36.22 9.17-6.84 0-17.24 1.38-37.27 4.62l-2.27.37c-24.5 3.99-31.65 5-37.46 5-3.49 0-4.08-.08-19.54-2.8-3.56-.64-6.32-1.1-9-1.5-20.23-2.96-31-1.2-31.96 7.86-.1.85-.18 1.72-.29 2.81l-.27 2.73c-1.1 10.9-2.02 15.73-4.31 19.96-2.9 5.34-7.77 7.95-15.63 7.95-10.2 0-12.92.6-15.5 3.17.52-.51-5.03 5.85-8.16 8.7-2.75 2.5-14.32 12.55-15.77 13.83a341.27 341.27 0 0 0-6.54 5.92c-6.97 6.49-11.81 11.76-14.6 16.15-5.92 9.3-10.48 18.04-11.69 24.08-1.66 8.3 3.67 9.54 19.02 1.21a626.23 626.23 0 0 1 44.54-21.9c3.5-1.56 14.04-6.2 15.68-6.95 5.05-2.25 8.3-3.8 10.78-5.15l1.95-1.07 2.18-1.18c1.76-.94 3.38-1.76 5-2.55 18.1-8.72 34.48-10.46 50.33-1.2 22.89 13.34 38.28 37.02 38.28 56.44 0 19.12-.73 25.13-5.18 33.2a45.32 45.32 0 0 1-4.94 7.12c-6.47 7.77-11.81 16.2-12.76 21.27-1.2 6.34 4.69 7.03 20.17-.05 13.31-6.08 22.4-14.95 28.5-26.32a80.51 80.51 0 0 0 6.1-15.13c.9-2.98 3.17-11.65 3.41-12.48a29.02 29.02 0 0 1 1.75-4.83c7.47-14.93 21.09-30.5 36.25-37.24 7.61-3.38 13-9.65 19.4-20.79.84-1.48 4.26-7.64 5.14-9.17 3.52-6.1 6.22-9.7 9.37-11.98 10.15-7.4 28.7-11.1 50.29-11.1 7.52 0 16.54-1.24 27.51-3.58a420.1 420.1 0 0 0 14.96-3.52c-1.3.33 15.54-3.98 19.42-4.89 14.15-3.33 41.07-5.01 64.11-5.01 17.36 0 27.82-9.23 38.53-38.67 6.62-18.21 6.62-26.37 2.69-34.35l-1.18-2.37A13.36 13.36 0 0 1 587.5 58c0-4.03 0-4.01 2.5-24.56.46-3.73.8-6.74 1.12-9.64.9-8.45 1.38-15.2 1.38-20.8 0-.94-.02-1.94-.04-3h1c.03 1.06.04 2.06.04 3 0 5.65-.48 12.43-1.39 20.9-.3 2.91-.66 5.93-1.11 9.66-2.5 20.45-2.5 20.47-2.5 24.44 0 1.97.45 3.57 1.45 5.68.24.51 1.16 2.35 1.17 2.36 4.06 8.24 4.06 16.68-2.65 35.13-10.84 29.8-21.63 39.33-39.47 39.33-22.96 0-49.83 1.68-63.89 4.99-3.86.9-20.69 5.2-19.4 4.88a421.05 421.05 0 0 1-14.99 3.53c-11.04 2.35-20.11 3.6-27.72 3.6-21.4 0-39.76 3.67-49.7 10.9-3 2.19-5.64 5.7-9.1 11.68-.87 1.52-4.29 7.68-5.14 9.17-6.49 11.3-12 17.71-19.86 21.2-14.9 6.63-28.38 22.03-35.75 36.77a28.17 28.17 0 0 0-1.69 4.67c-.23.8-2.5 9.49-3.4 12.5a81.48 81.48 0 0 1-6.19 15.3c-6.2 11.56-15.44 20.58-28.96 26.76-16.1 7.36-23 6.55-21.58-1.04 1-5.29 6.4-13.83 12.99-21.73a44.33 44.33 0 0 0 4.82-6.96c4.35-7.88 5.06-13.77 5.06-32.72 0-19.04-15.19-42.4-37.72-55.55-15.57-9.08-31.62-7.38-49.45 1.21a132.9 132.9 0 0 0-7.14 3.71l-1.95 1.07a158.83 158.83 0 0 1-10.85 5.19c-1.65.74-12.18 5.38-15.69 6.95a625.25 625.25 0 0 0-44.46 21.86c-15.95 8.66-22.37 7.16-20.48-2.29 1.24-6.2 5.83-15.02 11.82-24.42 2.85-4.48 7.74-9.8 14.77-16.34 1.98-1.85 4.12-3.79 6.56-5.94 1.46-1.29 13.02-11.33 15.75-13.82 3.09-2.8 8.6-9.14 8.14-8.67 2.82-2.82 5.75-3.46 16.2-3.46 7.5 0 12.04-2.43 14.75-7.42 2.2-4.07 3.11-8.84 4.2-19.59l.26-2.73.3-2.81c.56-5.42 4.47-8.5 11.23-9.6 5.44-.88 12.51-.51 21.86.86 2.7.4 5.47.86 9.04 1.49 15.33 2.7 15.96 2.8 19.36 2.8 5.73 0 12.9-1.03 37.3-5l2.27-.36c20.1-3.26 30.52-4.64 37.43-4.64 9.95 0 23.54-3.18 35.78-9.08 15.57-7.5 28.09-18.73 35.78-33.65 5.02-9.75 7-19.82 6.51-30.67-.37-8.37-1.96-16.08-5.23-27.57L379.27 0zm13.68 0h1.02c.78 3.9 1.92 8.7 3.51 14.88 3.63 14.05 3.06 27.03-.75 38.77a61 61 0 0 1-11.35 20.68 138.36 138.36 0 0 1-19.32 18.77c-11.32 9.02-23.36 15.49-35.95 18.39a258.63 258.63 0 0 1-22.57 4.07c-3.17.44-6.36.85-10.3 1.32l-9.39 1.12c-11.53 1.41-17.45 2.55-21.64 4.46-9.28 4.21-28.35 6.04-49.21 6.04-1.37 0-2.8-.12-4.3-.35-2.62-.41-5-1.03-9.14-2.29-7.34-2.21-9.63-2.75-12.63-2.56-3.9.23-6.63 2.29-8.47 6.89-1.86 4.66-2.42 7.53-3.34 14.98-1.1 8.98-2.87 12.12-9.97 14.3a40.12 40.12 0 0 0-6.8 2.66c-.63.33-1.16.64-1.76 1.02l-1.34.86c-1.9 1.14-3.86 1.49-9.25 1.49-3.2 0-8.83-.55-9.51-.39-1.22.28-.75-.14-7.14 6.24-1.5 1.5-3.49 3.18-6.32 5.37-1.52 1.18-7.16 5.43-7.94 6.03-4.96 3.78-8.33 6.6-11.06 9.38-4.88 4.98-6.85 9.15-5.56 12.7 1.34 3.67 4.07 4.42 8.9 2.82a55.72 55.72 0 0 0 7.77-3.48c1.5-.77 7.78-4.13 9.37-4.96a116.8 116.8 0 0 1 12.31-5.68 162.2 162.2 0 0 0 11.04-4.84c2.04-.97 10.74-5.16 13-6.22 4.41-2.1 8.1-3.78 11.65-5.29 17.14-7.3 29.32-9.9 37.67-6.65l5.43 2.1c2.3.88 4.17 1.62 6.02 2.38a150.9 150.9 0 0 1 13.07 6c18.34 9.63 30.35 22.13 34.79 39.87 6.96 27.85 3.6 45.53-8.08 62.4-3.97 5.75-3.52 9.2.06 8.97 4.14-.28 10.21-4.95 15.11-12.52 3.1-4.8 5.1-10.45 8.05-21.53l1.69-6.35c.66-2.47 1.24-4.52 1.83-6.5 4.93-16.56 11-27.28 21.56-34.76 7.15-5.06 23.73-15.5 25.48-16.75 6.74-4.81 10.53-9.44 14.34-18 7.74-17.44 21.09-24.34 44.47-24.34 9.36 0 17.91-1.13 29.53-3.49a624.86 624.86 0 0 0 6.2-1.28c2.4-.5 4.07-.84 5.66-1.13 4.03-.74 7.04-1.1 9.61-1.1 4.44 0 9.39-1 31.39-5.99l2.95-.66c16.34-3.67 25.64-5.35 31.66-5.35 1.54 0 2.4.01 6.4.1 7.8.15 12.27.13 17.33-.2 16.41-1.06 26.73-5.36 29.8-14.56a87.1 87.1 0 0 1 3.55-8.83c-.15.31 2.29-4.96 2.9-6.38 5.38-12.3 5.57-21.92-1.44-39.44a86.4 86.4 0 0 1-5.26-20.72c-1.61-11.98-1.38-23.14.1-40.35l.2-2.12h1l-.2 2.2c-1.48 17.15-1.7 28.24-.11 40.14a85.4 85.4 0 0 0 5.2 20.47c7.1 17.78 6.91 27.67 1.43 40.22-.62 1.43-3.06 6.72-2.91 6.4a86.17 86.17 0 0 0-3.52 8.73c-3.23 9.72-13.9 14.15-30.68 15.24-5.1.33-9.58.35-17.42.2-3.98-.09-4.84-.1-6.37-.1-5.91 0-15.18 1.67-31.44 5.32l-2.95.67c-22.16 5.02-27.05 6.01-31.61 6.01-2.5 0-5.45.36-9.43 1.09-1.58.29-3.25.62-5.64 1.11a4894.21 4894.21 0 0 0-6.2 1.29c-11.68 2.37-20.3 3.51-29.73 3.51-23.02 0-36 6.71-43.53 23.66-3.9 8.8-7.82 13.58-14.7 18.5-1.78 1.27-18.36 11.7-25.48 16.75-10.34 7.32-16.3 17.87-21.19 34.23-.58 1.96-1.15 4-1.82 6.47l-1.69 6.35c-2.98 11.18-5 16.9-8.17 21.81-5.05 7.81-11.37 12.68-15.89 12.98-4.7.31-5.3-4.23-.94-10.53 11.52-16.64 14.82-34.03 7.92-61.6-4.35-17.42-16.16-29.72-34.27-39.22-4-2.1-8.2-4-12.99-5.97-1.84-.75-3.7-1.49-6-2.38l-5.43-2.08c-8.03-3.12-20.02-.58-36.92 6.63-3.52 1.5-7.21 3.19-11.61 5.27l-13 6.22c-4.71 2.22-8.16 3.75-11.11 4.88a115.87 115.87 0 0 0-12.21 5.63c-1.58.83-7.86 4.18-9.37 4.96a56.55 56.55 0 0 1-7.9 3.54c-5.3 1.75-8.62.85-10.17-3.43-1.46-4.02.66-8.5 5.8-13.74 2.75-2.82 6.16-5.66 11.15-9.48.79-.6 6.43-4.85 7.94-6.02a66.96 66.96 0 0 0 6.23-5.28c6.74-6.74 6.1-6.16 7.61-6.51.87-.2 6.69.36 9.74.36 5.22 0 7.03-.32 8.74-1.35l1.31-.84c.62-.4 1.18-.72 1.84-1.07a41.07 41.07 0 0 1 6.96-2.72c6.64-2.04 8.22-4.84 9.28-13.47.93-7.53 1.5-10.47 3.4-15.24 1.99-4.95 5.04-7.26 9.34-7.51 3.17-.2 5.5.35 12.97 2.6a63.54 63.54 0 0 0 9.02 2.26c1.45.22 2.83.34 4.14.34 20.71 0 39.7-1.82 48.8-5.96 4.32-1.96 10.29-3.1 21.93-4.53l9.4-1.12c3.92-.48 7.11-.88 10.27-1.32 8.16-1.14 15.4-2.43 22.49-4.06 12.42-2.86 24.33-9.26 35.55-18.2a137.4 137.4 0 0 0 19.18-18.64 60.02 60.02 0 0 0 11.15-20.32c3.76-11.57 4.32-24.36.75-38.23A284.86 284.86 0 0 1 392.95 0zM506.7 0h1.26c-.5.66-.9 1.18-1.17 1.51-3.95 4.96-6.9 7.92-9.82 9.57A10.02 10.02 0 0 1 492 12.5c-2.38 0-4.24.67-6.71 2.21l-2.65 1.71c-4.38 2.8-8.01 4.08-13.64 4.08-5.6 0-9.99-1.26-16.08-4.05a202.63 202.63 0 0 1-2.3-1.06l-2.18-.98c-1.6-.7-2.92-1.17-4.17-1.48a13.42 13.42 0 0 0-3.27-.43c-2.3 0-4.3-.68-11-3.37l-1.56-.62c-5-1.97-8.1-2.82-10.52-2.66-2.93.2-4.42 2.03-4.42 6.15 0 20.76-5.21 50.42-12.15 57.35-7.58 7.59-26.55 23.7-34.06 29.06-13.16 9.4-31.17 20.2-44.11 25.06a106.87 106.87 0 0 1-13.32 4.03c-3.28.78-6.6 1.43-11.25 2.24-.53.1-8.8 1.5-11.5 1.99-4.86.87-9.3 1.74-14 2.76-20.62 4.48-25.07 5.01-38.11 5.01-2.49 0-2.9-.07-14.05-2-2.42-.42-4.31-.73-6.15-1-8.11-1.19-13.83-1.36-17.64-.2-4.54 1.4-5.93 4.65-3.7 10.52 2.02 5.28 4.84 8.61 8.84 10.74 3.26 1.74 6.75 2.6 13.82 3.71 9.42 1.48 10.94 1.75 15.5 2.92a78.2 78.2 0 0 1 18.62 7.37c8.3 4.58 14.58 11.5 19.98 20.89 2.73 4.73 9.46 19.33 10.54 21.19 3.4 5.85 6.26 6.63 10.89 2 4.95-4.94 10.35-8.37 21.13-14.06.47-.25 2.06-1.1 2.12-1.12 7.98-4.21 11.92-6.51 15.87-9.54 5.11-3.9 8.66-8.1 10.77-13.11 8.52-20.24 20.75-33.31 32.46-33.31l5.5.03c10.53.08 17.35.02 24.9-.31 13.66-.62 23.78-2.09 29.39-4.67 5.85-2.7 13.42-5.49 24.18-9.02 3.46-1.14 6.29-2.05 12.7-4.1 7.7-2.45 11.08-3.54 15.17-4.9a1059.43 1059.43 0 0 1 11.33-3.72c3.67-1.2 5.96-2 8.03-2.78a59.88 59.88 0 0 0 6.66-2.94c1.87-.98 3.76-2.1 5.86-3.5 3.48-2.33 6.15-3.13 12.04-4.13l1.15-.2c5.71-1.01 9-2.3 12.76-5.63 7.82-6.96 8.58-23.18 3.84-44.52-1.7-7.67-2.1-19.28-1.57-35.47A837.22 837.22 0 0 1 546.76 0h1l-.15 3.06c-.32 6.42-.53 11.02-.68 15.62-.51 16.1-.12 27.65 1.56 35.21 4.82 21.68 4.04 38.2-4.16 45.48-3.91 3.48-7.37 4.84-13.24 5.87l-1.16.2c-5.76.99-8.32 1.75-11.65 3.98a63.73 63.73 0 0 1-5.96 3.56 60.86 60.86 0 0 1-6.77 2.99c-2.09.79-4.39 1.58-8.07 2.79a5398.31 5398.31 0 0 1-11.32 3.71c-4.1 1.37-7.48 2.46-15.18 4.92-6.42 2.04-9.24 2.95-12.7 4.08-10.73 3.53-18.27 6.3-24.07 8.98-5.76 2.66-15.97 4.14-29.77 4.77-7.56.33-14.4.39-24.95.31l-5.49-.03c-11.19 0-23.16 12.79-31.54 32.7-2.19 5.19-5.84 9.52-11.08 13.52-4.02 3.07-7.99 5.39-16.01 9.62l-2.12 1.12c-10.7 5.65-16.04 9.04-20.9 13.9-5.14 5.14-8.75 4.15-12.45-2.22-1.12-1.92-7.85-16.5-10.54-21.2-5.33-9.24-11.48-16.02-19.6-20.5a77.2 77.2 0 0 0-18.4-7.28c-4.5-1.17-6.02-1.43-15.4-2.9-7.17-1.12-10.74-2-14.13-3.81-4.22-2.25-7.2-5.77-9.3-11.27-2.43-6.39-.78-10.26 4.34-11.83 4-1.22 9.82-1.05 18.08.17 1.84.27 3.74.58 6.17 1 11.02 1.9 11.48 1.98 13.88 1.98 12.96 0 17.35-.52 37.9-4.99 4.71-1.02 9.16-1.9 14.03-2.77 2.71-.48 10.98-1.9 11.5-1.98 4.64-.81 7.95-1.46 11.2-2.23 4.55-1.07 8.76-2.34 13.2-4 12.83-4.81 30.79-15.59 43.88-24.94 7.47-5.33 26.4-21.4 33.94-28.94C407.3 61.98 412.5 32.49 412.5 12c0-4.61 1.86-6.9 5.35-7.15 2.63-.18 5.8.7 10.96 2.73l1.56.62c6.53 2.62 8.53 3.3 10.63 3.3 1.14 0 2.3.16 3.5.46 1.32.33 2.68.82 4.34 1.53a90.97 90.97 0 0 1 3.34 1.52l1.15.54c5.98 2.73 10.23 3.95 15.67 3.95 5.41 0 8.87-1.21 13.1-3.92.2-.13 2.1-1.38 2.66-1.72 2.62-1.63 4.64-2.36 7.24-2.36 1.47 0 2.94-.43 4.47-1.3 2.78-1.56 5.67-4.45 9.54-9.31l.7-.89zM324.54 600h-2.03c.49-2.96.91-6.2 1.28-9.66.44-4.1.76-8.25.98-12.21.08-1.39.14-2.65-.35-7.29-.47-1.94-.93-4.14-1.36-6.54-2.01-11.26-2.66-22.9-1.14-33.78a60.76 60.76 0 0 1 5.18-17.95 70.78 70.78 0 0 1 12.6-18.22c3.38-3.6 5.53-5.5 11.83-10.79 4.5-3.78 6.35-5.56 7.52-7.5.64-1.07.95-2.06.95-3.06 0-1.75 0-1.74-.75-9.23-.36-3.7-.57-6.3-.68-8.96-.5-12.1 1.62-19.6 8.11-21.76 15.9-5.3 25.89-12.1 33.45-25.54C409.6 390.65 425.85 376 436 376c12.36 0 20-1.96 29.41-8.8 6.76-4.92 9.5-6.6 12.47-7.46 2.22-.64 3.8-.74 9.12-.74 1.86 0 3.53-.83 5.57-2.62 1.08-.96 5.11-5.12 5.6-5.6 6.04-5.85 11.98-8.78 20.83-8.78 2.45 0 4.54.04 7.32.12 7.51.23 8.87.17 11.27-.7 3.03-1.1 5.53-3.03 14.75-11.17 8-7.06 10.72-8.92 22.87-16.47 1.44-.9 2.59-1.63 3.69-2.37a69.45 69.45 0 0 0 9.46-7.5c4.12-3.88 8.02-7.85 11.64-11.9v2.98a201.58 201.58 0 0 1-10.27 10.38c-3.18 3-6.2 5.35-9.72 7.7-1.12.76-2.28 1.5-3.75 2.4-12.05 7.5-14.71 9.32-22.6 16.28-9.46 8.35-12.01 10.32-15.39 11.55-2.74 1-4.19 1.06-12.01.82-2.76-.08-4.83-.12-7.26-.12-8.27 0-13.75 2.7-19.43 8.22-.44.43-4.52 4.64-5.68 5.66-2.37 2.09-4.46 3.12-6.89 3.12-5.1 0-6.6.1-8.56.66-2.67.78-5.29 2.37-11.85 7.15-9.8 7.13-17.85 9.19-30.59 9.19-9.22 0-24.96 14.2-34.13 30.49-7.84 13.94-18.24 21.02-34.55 26.46-5.31 1.77-7.21 8.51-6.75 19.78.1 2.6.31 5.19.68 8.84.75 7.62.75 7.58.75 9.43 0 1.38-.42 2.73-1.24 4.09-1.33 2.2-3.26 4.07-7.94 8-6.25 5.24-8.36 7.12-11.67 10.63a68.8 68.8 0 0 0-12.25 17.71 58.8 58.8 0 0 0-5 17.36c-1.49 10.66-.85 22.09 1.13 33.15.43 2.37.88 4.53 1.33 6.44.16.66.3 1.25.6 4.06a249.3 249.3 0 0 1-1.17 16.12c-.37 3.37-.78 6.53-1.25 9.44zm-13.4 0h-1.05l.12-.28c3.07-7.16 4.29-11.83 4.29-18.72 0-3.57-.07-4.93-.76-15.65-.77-12.04-1-19.64-.55-28.3.58-11.5 2.4-22.1 5.81-32.16 1.3-3.8 2.8-7.5 4.55-11.1 3.46-7.14 6.83-12.39 10.42-16.6a59.02 59.02 0 0 1 4.35-4.56c.43-.4 3-2.8 3.67-3.45 5.72-5.6 7.51-11.52 7.51-29.18 0-18.84 2.9-23.77 15.82-28.24 1.09-.37 1.92-.67 2.77-.98a51.3 51.3 0 0 0 6.1-2.7c4.95-2.6 9.64-6.22 14.44-11.42 25.5-27.63 37.15-35.16 56.37-35.16 8.28 0 14.54-1.95 22-6.3 1.78-1.03 13.82-8.82 18.16-11.27 2.83-1.59 5.66-3.03 8.63-4.39 7.92-3.6 13.97-4.45 26.6-4.8 7.53-.2 10.7-.49 14.26-1.58 4.55-1.4 8.06-4 10.93-8.43 2.2-3.41 6.85-7.08 14.66-12.06 1.61-1.03 3.27-2.05 5.65-3.5 9.53-5.85 11.56-7.13 14.81-9.57 5.34-4 9.3-8.37 13.68-14.77a204.2 204.2 0 0 0 5.62-8.75v1.9c-1.97 3.17-3.4 5.38-4.8 7.42-4.42 6.48-8.46 10.92-13.9 15-3.29 2.46-5.32 3.75-14.89 9.61a375.06 375.06 0 0 0-5.63 3.5c-7.7 4.9-12.26 8.52-14.36 11.76-3 4.63-6.7 7.39-11.48 8.85-3.68 1.12-6.9 1.42-14.53 1.63-12.5.34-18.44 1.18-26.2 4.7a111.08 111.08 0 0 0-8.56 4.35c-4.3 2.43-16.34 10.22-18.15 11.27-7.6 4.43-14.03 6.43-22.5 6.43-18.87 0-30.3 7.4-55.63 34.84-4.88 5.28-9.67 8.97-14.7 11.62-2 1.05-4 1.92-6.23 2.75-.86.32-1.7.62-5.37 1.87-5.08 1.76-7.44 3.25-9.28 6.37-2.23 3.78-3.29 9.94-3.29 20.05 0 17.9-1.87 24.07-7.8 29.89-.69.67-3.27 3.06-3.69 3.46a58.04 58.04 0 0 0-4.28 4.49c-3.53 4.14-6.86 9.32-10.28 16.38a95.19 95.19 0 0 0-4.5 10.99c-3.38 9.97-5.18 20.48-5.76 31.9-.44 8.6-.22 16.17.55 28.17.69 10.76.76 12.12.76 15.72 0 6.35-1.02 10.87-4.35 19zm25.08 0h-1c-.04-4.73.06-9.39.28-15.02.26-6.41-.4-11.79-2.53-24.37l-.31-1.86c-2.12-12.55-2.76-19.35-1.97-26.47 1.03-9.25 4.75-16.68 12-22.67 22.04-18.2 29.81-30.18 29.81-44.61 0-2.6-.3-4.81-.98-8.17-.97-4.79-1.1-5.68-.97-7.57.2-2.56 1.27-4.7 3.56-6.72 2.67-2.35 7.05-4.6 13.72-7.01 9.72-3.5 15.52-9.18 24.3-21.57l1.78-2.5c4.48-6.33 7.1-9.63 10.43-12.78 4.31-4.07 8.98-6.77 14.54-8.17 13.3-3.32 20.37-5.47 25.34-7.64a49.5 49.5 0 0 0 5.28-2.7c1.1-.65 1.75-1.04 4.24-2.6 2.7-1.68 5.22-2.08 11.38-2.28 5.44-.18 7.9-.43 10.97-1.41a21.47 21.47 0 0 0 9.54-6.22c4.87-5.3 10.03-7.61 17.79-8.9 1.07-.18 1.88-.3 3.86-.58 6.9-.97 9.94-1.69 13.48-3.62 4.5-2.45 6.79-4.44 23.46-19.68l3.14-2.85c9.65-8.71 16.12-13.83 21.42-16.48 4.25-2.12 7.6-4.69 11.22-8.6v1.45c-3.42 3.57-6.69 6-10.78 8.05-5.18 2.59-11.61 7.67-21.2 16.32l-3.12 2.85c-16.8 15.35-19.05 17.3-23.66 19.82-3.68 2-6.8 2.75-13.82 3.73-1.97.28-2.78.4-3.84.57-7.56 1.26-12.52 3.48-17.21 8.6a22.47 22.47 0 0 1-9.97 6.5c-3.2 1-5.72 1.27-11.25 1.45-5.98.2-8.39.57-10.89 2.13a144 144 0 0 1-4.25 2.61 50.48 50.48 0 0 1-5.39 2.75c-5.04 2.2-12.15 4.37-25.5 7.7-9.74 2.44-15.26 7.65-24.4 20.56l-1.77 2.5c-8.9 12.54-14.82 18.34-24.78 21.93-6.57 2.36-10.85 4.57-13.4 6.82-2.1 1.86-3.05 3.74-3.22 6.04-.13 1.76 0 2.63.95 7.3.7 3.42 1 5.7 1 8.37 0 14.79-7.93 27-30.18 45.39-7.03 5.8-10.64 13-11.64 22-.78 7-.14 13.73 1.96 26.2l.32 1.85c2.15 12.65 2.8 18.07 2.54 24.58-.22 5.57-.32 10.2-.28 14.98zM95.9 600h-2.04c.68-3.82 1.14-8.8 1.61-15.98.2-3.11.27-4.06.39-5.6 1.3-17.54 4.04-27.14 11.5-33.2 4.65-3.77 7.22-8.92 8.67-16 .51-2.52.7-3.87 1.33-9.17.66-5.5 1.16-8.06 2.24-10.36 1.45-3.09 3.82-4.69 7.39-4.69 14.28 0 38.48 9.12 53.6 20.2 8.66 6.35 21.26 13.32 31.74 17.11 13.03 4.71 21.89 4.41 24.75-1.73 1.7-3.64 1.92-4.11 2.65-5.77 2.93-6.67 4.69-12.2 5.25-17.5.23-2.17.24-4.23.02-6.2-.32-2.75-1.42-4.55-4.08-7.35l-1.32-1.37a30.59 30.59 0 0 1-2.41-2.79 30.37 30.37 0 0 1-2.5-4.07l-1.13-2.14c-1.62-3.1-2.68-4.6-4.12-5.56-5.26-3.5-14.8-5.5-28.55-6.83a272.42 272.42 0 0 0-9.04-.71l-2.18-.17c-9.57-.73-15.12-1.56-19.06-3.2C156.57 471.07 136 450.5 136 440c0-5.34 1.74-9.53 5.47-14.13 1.98-2.44 11.12-11.71 12.79-13.54 4.52-4.97 10.16-9.54 17.68-14.66 2.8-1.9 14.78-9.6 17.49-11.49a50.54 50.54 0 0 0 6.34-5.43c1.53-1.5 6.96-7.13 7.12-7.3 7.18-7.3 12.7-11.56 19.74-14.38 3.36-1.34 8.13-2.79 17.45-5.38a9577.18 9577.18 0 0 1 11.78-3.28 602.6 602.6 0 0 0 12.67-3.7c20.4-6.24 34-12.08 40.79-18.44 8.74-8.2 11.78-13.84 15.73-26.02 2.02-6.22 3.09-9.04 5.07-12.72 9.54-17.71 28.71-39.37 43.5-45.45C383.77 238.25 389 232.34 389 226c0-2.89 2.73-8.4 6.83-13.73 4.76-6.2 10.65-11.36 16.75-14.18 12.5-5.77 33.5-10.09 47.42-10.09 5.32 0 9.83-1.5 16.42-4.89 9.2-4.71 10.1-5.11 13.58-5.11 10.42 0 32.06-2.55 45.76-5.97l3.88-.98 3.47-.89c2.6-.66 4.33-1.08 5.93-1.43 3.9-.86 6.76-1.23 9.58-1.17 2.74.06 5.47.52 8.67 1.48 4.56 1.37 13.71-.9 22.87-5.68a68.07 68.07 0 0 0 9.84-6.2v2.4c-11.09 8.14-25.76 13.66-33.29 11.4a29.72 29.72 0 0 0-8.13-1.4c-2.63-.05-5.36.3-9.11 1.12a238 238 0 0 0-9.33 2.3l-3.9.99C522.38 177.43 500.58 180 490 180c-2.99 0-3.91.4-12.67 4.89-6.85 3.51-11.61 5.11-17.33 5.11-13.65 0-34.35 4.26-46.58 9.9-5.78 2.67-11.42 7.62-16 13.58-3.85 5.02-6.42 10.2-6.42 12.52 0 7.27-5.8 13.82-20.62 19.92-14.27 5.88-33.16 27.21-42.5 44.55-1.9 3.55-2.95 6.28-4.93 12.4-4.05 12.47-7.23 18.39-16.27 26.86-7.08 6.64-20.87 12.57-41.57 18.89a604.52 604.52 0 0 1-12.7 3.71 1495.1 1495.1 0 0 1-11.8 3.28c-9.24 2.58-13.97 4.01-17.24 5.32-6.73 2.69-12.05 6.8-19.05 13.92-.15.15-5.6 5.8-7.15 7.32a52.4 52.4 0 0 1-6.6 5.65c-2.74 1.92-14.75 9.63-17.5 11.5-7.4 5.04-12.94 9.52-17.33 14.35-1.72 1.9-10.8 11.11-12.71 13.46-3.47 4.26-5.03 8.03-5.03 12.87 0 9.5 20 29.5 33.38 35.08 3.67 1.53 9.1 2.34 18.45 3.05a586.23 586.23 0 0 0 4.34.32c3.24.23 5.07.37 6.93.55 14.08 1.37 23.82 3.4 29.45 7.17 1.82 1.2 3.02 2.91 4.8 6.29l1.11 2.13a28.55 28.55 0 0 0 2.34 3.81c.62.83 1.3 1.6 2.26 2.61.23.24 1.1 1.16 1.32 1.37 2.93 3.09 4.24 5.23 4.61 8.5.24 2.12.23 4.33-.01 6.64-.59 5.55-2.4 11.25-5.41 18.1-.74 1.67-.96 2.15-2.66 5.8-3.49 7.47-13.33 7.8-27.25 2.77-10.67-3.86-23.43-10.92-32.25-17.38C164.62 515.96 140.82 507 127 507c-5 0-6.4 3.02-7.64 13.29a99.03 99.03 0 0 1-1.36 9.33c-1.53 7.5-4.3 13.04-9.37 17.16-6.87 5.58-9.5 14.78-10.77 31.8-.11 1.52-.18 2.47-.38 5.57-.46 7.01-.91 11.99-1.57 15.85zm8.05 0h-1.02c.29-1.41.58-2.94.9-4.59l1.05-5.62c2.5-13.3 4.2-19.92 6.68-24.05 1.7-2.84 3.68-5.5 8.05-11.03 8.21-10.36 10.88-14.55 10.88-18.71l-.02-1.69c-.02-1.78-.02-2.7.02-3.77.21-5.05 1.47-8.2 4.64-9.4 3.92-1.5 10.39.44 20.12 6.43 9.56 5.88 17.53 10.7 25.91 15.66 1.31.78 14.27 8.41 17.67 10.45a714.21 714.21 0 0 1 6.42 3.9c13.82 8.5 38.94 5.05 46.3-7.83 3.6-6.28 4.54-8.52 7.78-17.32a82.3 82.3 0 0 1 1.18-3.07 42.27 42.27 0 0 1 4.06-7.64c9.33-13.98 14.92-26.1 14.92-36.72 0-3.66.75-6.62 3.36-14.85.52-1.64.83-2.66 1.15-3.73 3.64-12.23 3.04-19.12-4.29-24a23.1 23.1 0 0 0-9.98-3.78c-7.2-.93-14.49 1.17-23.91 5.88-1.55.78-6.64 3.44-7.6 3.93a62.6 62.6 0 0 0-4.14 2.3l-4.4 2.66c-11.62 6.92-20.4 9.18-32.81 6.08-3.32-.84-6.24-1.4-13.1-2.64-13.25-2.39-18.7-3.75-23.33-6.46-6.23-3.67-7.46-9.02-2.88-16.65A93.1 93.1 0 0 1 172 415.42a157 157 0 0 1 8.32-7.66c-.07.05 6.16-5.3 7.82-6.77a85.12 85.12 0 0 0 6.5-6.33c7.7-8.46 12.78-13.36 20.08-18.57 9.94-7.1 21.4-12.36 35.18-15.58 37.03-8.64 51-12.7 58.83-17.93 8.6-5.73 21.3-24.77 36.84-54.81 5.22-10.1 12.27-18.4 21.13-25.71 5.13-4.24 9.56-7.25 17.55-12.23 7.42-4.62 9.62-6.14 11.38-8.16a21.15 21.15 0 0 0 2.95-4.87c.61-1.3 2.87-6.47 3-6.77 1.36-3 2.56-5.4 3.95-7.73 6.53-10.97 16.03-18 31.4-20.8 12.73-2.3 19.85-2.7 29.68-2.3 3.25.13 4.13.16 5.6.14 5.15-.07 9.71-1.04 16.61-3.8 20.74-8.3 38.75-12.04 59.19-12.04 3.05 0 6.03.15 10.48.48l2.09.16c12.45.96 18.08.96 25.34-.63a49.65 49.65 0 0 0 14.09-5.45v1.15a50.52 50.52 0 0 1-13.88 5.28c-7.38 1.61-13.08 1.61-25.63.65l-2.08-.16c-4.43-.33-7.39-.48-10.41-.48-20.3 0-38.2 3.72-58.81 11.96-7.01 2.8-11.7 3.8-16.97 3.88-1.5.02-2.39-.01-5.66-.14-9.76-.4-16.8-.01-29.47 2.3-15.06 2.73-24.32 9.58-30.71 20.31a72.8 72.8 0 0 0-3.9 7.63c-.12.28-2.39 5.47-3.01 6.79a22 22 0 0 1-3.1 5.1c-1.86 2.13-4.07 3.66-11.6 8.35-7.95 4.96-12.35 7.95-17.44 12.15-8.76 7.23-15.73 15.43-20.89 25.4-15.61 30.2-28.36 49.32-37.16 55.19-7.98 5.32-21.97 9.39-59.17 18.07-13.65 3.18-24.98 8.39-34.82 15.42-7.22 5.16-12.27 10.01-19.92 18.43a86.07 86.07 0 0 1-6.57 6.4c-1.67 1.48-7.91 6.83-7.84 6.77-3.27 2.84-5.8 5.16-8.26 7.62a92.1 92.1 0 0 0-14.27 18.13c-4.3 7.16-3.22 11.89 2.53 15.26 4.47 2.63 9.88 3.99 23.24 6.39a185.7 185.7 0 0 1 12.92 2.6c12.11 3.03 20.64.84 32.06-5.96l4.4-2.65c1.66-1 2.96-1.73 4.2-2.35.95-.48 6.04-3.14 7.6-3.92 9.59-4.8 17.04-6.94 24.49-5.98a24.1 24.1 0 0 1 10.4 3.93c7.82 5.21 8.45 12.52 4.7 25.13-.32 1.07-.64 2.1-1.16 3.74-2.57 8.12-3.31 11.04-3.31 14.55 0 10.88-5.66 23.14-15.08 37.28a41.28 41.28 0 0 0-3.97 7.46c-.37.9-.73 1.82-1.18 3.04-3.25 8.85-4.21 11.13-7.84 17.47-7.67 13.42-33.43 16.95-47.7 8.18a578.4 578.4 0 0 0-6.4-3.89c-3.4-2.04-16.36-9.67-17.67-10.45-8.38-4.97-16.36-9.78-25.92-15.66-9.5-5.85-15.7-7.7-19.24-6.36-2.68 1.02-3.8 3.82-4 8.51a61.12 61.12 0 0 0-.02 3.72l.02 1.7c0 4.5-2.69 8.73-11.52 19.87-3.92 4.95-5.87 7.59-7.55 10.39-2.39 3.97-4.08 10.56-6.56 23.72l-1.05 5.62-.86 4.4zm10.5 0h-1c.03-.34.04-.68.04-1 0-12.39 8.48-33.57 19.16-43.37a26.18 26.18 0 0 0 3.67-4.17 35.8 35.8 0 0 0 2.88-4.9c.36-.72 1.75-3.66 2.1-4.36 3.22-6.29 6.84-6.54 16.97.39 1.34.9 6.07 4.16 6.4 4.38 2.62 1.8 4.67 3.2 6.7 4.56 5.03 3.39 9.37 6.2 13.51 8.7 14.33 8.67 25.49 13.27 34.11 13.27 16.86 0 32.71-5.95 39.6-14.8 1.59-2.04 3.2-5.17 5.06-9.63.8-1.92 1.64-4.06 2.67-6.8l2.74-7.33c4.66-12.44 7.76-19.06 11.56-23.27 7.9-8.79 14.87-36 14.87-52.67 0-1.9.17-3.11 1.02-8.27.37-2.2.58-3.6.74-5.07.63-5.51.21-9.46-1.68-12.39-4.6-7.1-19.7-9.23-38.46-4.78a100.57 100.57 0 0 0-18.94 6.3c-5.17 2.37-17.11 9.74-16.5 9.4-6.72 3.64-12.97 4.15-24.8 1.3-29.55-7.14-30.43-8.62-15.26-26.81 17.44-20.93 47.12-46.18 56.38-46.18 9.92 0 53.84-11.98 65.78-17.95 9.46-4.73 24.32-21.18 36.82-37.85.71-.95 13.5-21.6 19.2-29.6 9.35-13.13 18.22-22.55 26.95-27.53 7.29-4.17 13.16-10.28 18.8-18.73 1.93-2.9 10.52-17.65 12.73-20.41 1.54-1.93 3-3.21 4.52-3.89 14.07-6.25 24.22-9.04 39.2-9.04h29c4.05 0 7.36-.4 22.93-2.5l4.3-.57c9.92-1.3 16.57-1.93 21.77-1.93 1.66 0 2.95.01 6.03.04 18.61.19 28.55-.48 44.86-4.03 3.1-.67 6.13-1.78 9.11-3.31v1.12a37.96 37.96 0 0 1-8.9 3.17c-16.4 3.56-26.4 4.24-45.08 4.05-3.08-.03-4.36-.04-6.02-.04-5.15 0-11.76.63-21.64 1.92l-4.3.58c-15.64 2.11-18.94 2.5-23.06 2.5h-29c-14.81 0-24.84 2.75-38.8 8.96-1.34.6-2.69 1.78-4.14 3.6-2.16 2.68-10.72 17.39-12.68 20.33-5.72 8.57-11.7 14.8-19.13 19.04-8.57 4.9-17.36 14.23-26.63 27.24-5.68 7.97-18.47 28.64-19.22 29.63-12.6 16.8-27.52 33.32-37.18 38.15-12.06 6.03-56.14 18.05-66.22 18.05-8.82 0-38.39 25.15-55.62 45.82-14.6 17.52-14.19 18.21 14.74 25.2 11.6 2.8 17.6 2.3 24.09-1.2-.67.35 11.31-7.03 16.56-9.44 5.41-2.48 11.6-4.59 19.11-6.37 19.13-4.53 34.65-2.35 39.54 5.22 2.05 3.17 2.48 7.32 1.84 13.04a96.34 96.34 0 0 1-.75 5.13c-.84 5.08-1.01 6.29-1.01 8.1 0 16.9-7.03 44.33-15.13 53.33-3.68 4.09-6.76 10.65-11.37 22.96-.35.93-2.2 5.94-2.73 7.33-1.04 2.76-1.88 4.9-2.68 6.84-1.9 4.53-3.55 7.73-5.2 9.85-7.1 9.13-23.25 15.19-40.39 15.19-8.86 0-20.15-4.65-34.63-13.42-4.15-2.51-8.5-5.32-13.55-8.72a861.54 861.54 0 0 1-6.71-4.56l-6.4-4.39c-9.68-6.63-12.61-6.42-15.5-.75-.35.68-1.74 3.62-2.1 4.35a36.77 36.77 0 0 1-2.96 5.03c-1.12 1.57-2.37 3-3.81 4.33-10.47 9.6-18.84 30.51-18.84 42.63l-.03 1zm-29.65 0h-1.1c1.17-2.52 1.79-5.2 1.79-8 0-20 4.83-42.04 12.15-49.35 5.17-5.18 7.77-8.38 9.9-12.74 2.64-5.41 3.95-12 3.95-20.91 0-6.82 1.14-11.59 3.37-15.07 1.74-2.7 3.6-4.21 8.91-7.52a31.64 31.64 0 0 0 3.9-2.79c4.61-3.96 6.58-6.2 7.72-9.41 1.43-4.02.93-9.04-1.86-16.02a68.98 68.98 0 0 0-3.99-8.07l-.93-1.7a75.47 75.47 0 0 1-2.64-5c-5.16-10.71-3.77-18.9 7.68-29.78a204 204 0 0 1 26.81-21.55c3.96-2.69 16.8-10.8 19.24-12.5 1.99-1.4 4.33-3.3 7.77-6.3-.02 0 7.23-6.39 9.47-8.3 4.97-4.26 9.09-7.5 13.05-10.15 4.72-3.15 8.97-5.28 12.87-6.32 12.78-3.41 15.6-4.18 21.77-5.97 12.55-3.64 21.96-6.9 28.14-10a45.47 45.47 0 0 1 7.47-2.79c8.66-2.66 12.02-4.1 16.97-8.1 6.78-5.46 13.07-14.25 19.33-27.87 15.97-34.77 19.08-39.39 32.15-49.19 3.14-2.36 6.37-4.1 11.43-6.4l2.33-1.04c11.93-5.35 16.87-8.93 21.1-17.38 1.88-3.77 2.48-6.29 3.37-12.27.78-5.19 1.48-7.56 3.53-10.25 2.57-3.4 7.03-6.27 14.36-9.01 3.37-1.26 7.36-2.5 12.05-3.73 16.33-4.3 25.28-5.36 39.6-5.81 6.9-.22 9.5-.56 12.66-2 1.19-.54 2.36-1.23 3.58-2.11 3.7-2.7 8.14-4.54 13.24-5.67 5.71-1.27 10.69-1.54 18.7-1.45l2.35.02c2.82 0 6.8-1 19.7-4.69 10.83-3.08 15.95-4.31 19.3-4.31.82 0 1.9.13 3.55.41l5.01.9c9.82 1.68 17.44 1.89 25.15-.21 7.98-2.18 14.8-6.77 20.29-14.24V147c-5.47 7.04-12.21 11.42-20.03 13.55-7.88 2.15-15.63 1.94-25.58.23l-5-.9c-1.6-.26-2.64-.39-3.39-.39-3.2 0-8.32 1.22-19.74 4.48-12.35 3.53-16.3 4.52-19.26 4.52l-2.36-.02c-7.94-.1-12.85.17-18.47 1.42-4.97 1.11-9.3 2.9-12.88 5.5a21.4 21.4 0 0 1-3.75 2.22c-3.32 1.5-6 1.87-13.04 2.09-14.25.44-23.13 1.5-39.37 5.77a125.56 125.56 0 0 0-11.95 3.7c-7.17 2.7-11.49 5.46-13.93 8.68-1.9 2.52-2.58 4.76-3.33 9.8-.9 6.08-1.53 8.68-3.47 12.56a30.6 30.6 0 0 1-9.66 11.45c-3.12 2.26-5.95 3.73-11.93 6.4l-2.31 1.04c-5.01 2.27-8.18 3.99-11.25 6.29-12.9 9.68-15.93 14.17-31.85 48.8-6.31 13.76-12.7 22.68-19.6 28.25-5.08 4.1-8.53 5.57-17.3 8.27a44.64 44.64 0 0 0-7.33 2.73c-6.24 3.12-15.7 6.4-28.3 10.06a867.4 867.4 0 0 1-21.8 5.97c-3.77 1.01-7.93 3.1-12.56 6.19a137.35 137.35 0 0 0-12.95 10.07c-2.24 1.92-9.48 8.3-9.48 8.3a98.2 98.2 0 0 1-7.84 6.37c-2.46 1.72-15.32 9.83-19.26 12.5a203 203 0 0 0-26.69 21.45c-11.13 10.58-12.43 18.3-7.47 28.63a74.52 74.52 0 0 0 2.62 4.95l.94 1.7a69.84 69.84 0 0 1 4.03 8.17c2.88 7.2 3.4 12.46 1.89 16.73-1.22 3.43-3.28 5.77-8.02 9.84-1.14.97-2.32 1.8-5.3 3.67-3.92 2.45-5.69 3.89-7.31 6.42-2.13 3.3-3.22 7.89-3.22 14.53 0 9.05-1.34 15.79-4.05 21.34-2.19 4.49-4.85 7.77-10.1 13.01-7.07 7.07-11.85 28.9-11.85 48.65 0 2.8-.58 5.48-1.7 8zm282.54 0h-1.01l-1.1-5.8c-3.08-16.26-4.05-26.2-2.74-37.26.7-5.8.77-9.68.55-15.3-.18-4.45-.17-5.68.19-7.63.78-4.3 3.44-8.53 10.39-16.34 9.07-10.2 12.26-15.41 19.8-30.15 1.35-2.64 2.33-4.47 3.38-6.3.9-1.58 1.82-3.06 2.77-4.5 3.14-4.7 7.03-8.42 16.84-16.81 11.22-9.6 15.5-13.86 18.13-19.13.7-1.4 1.3-2.8 1.93-4.4a206 206 0 0 0 1.49-4.05c3.63-9.94 8.01-13.93 22.9-17.81 4.99-1.3 20.55-5.13 21.38-5.34 16.19-4.1 25.33-7.36 33.48-12.6 5.86-3.77 5.84-3.76 27.66-16.53l2.6-1.52c10.23-6 17.1-10.2 22.73-13.95a149.3 149.3 0 0 0 8.8-6.3 723.7 723.7 0 0 0 6.37-5.08A87.74 87.74 0 0 1 600 342.95v1.12a85.76 85.76 0 0 0-15.49 9.9c.18-.14-4.76 3.84-6.38 5.1a150.3 150.3 0 0 1-8.85 6.35c-5.65 3.76-12.53 7.96-22.78 13.97l-2.6 1.53c-21.8 12.75-21.78 12.74-27.63 16.5-8.27 5.32-17.49 8.61-33.78 12.73-.83.21-16.39 4.04-21.36 5.33-8.03 2.1-13.15 4.5-16.45 7.5-2.66 2.42-4 4.86-5.77 9.7l-1.5 4.07a51.12 51.12 0 0 1-1.96 4.47c-2.72 5.45-7.04 9.75-18.38 19.45-9.73 8.32-13.6 12.02-16.65 16.6a77.18 77.18 0 0 0-2.74 4.45c-1.05 1.81-2.01 3.63-3.35 6.25-7.58 14.81-10.82 20.08-19.96 30.36-6.83 7.7-9.4 11.78-10.15 15.86-.34 1.85-.34 3.04-.17 7.4.22 5.68.14 9.6-.55 15.47-1.3 10.92-.34 20.79 2.73 36.95l1.12 5.99zm-76.59 0h-2.1l1.39-4.3c1.04-3.3 1.93-6.78 2.68-10.4 2.65-12.73 3.27-23.63 3.27-41.3 0-5.71-1.86-9.75-4.13-9.75-2.94 0-6.96 5.61-10.93 17.08C271.14 579.68 258.3 593 238 593c-22.42 0-29.26-1.35-48.42-10.09a87.69 87.69 0 0 1-9.42-5.04c-2.95-1.8-12.78-8.57-14.84-9.72-4.2-2.36-7-2.71-9.72-.99-.63.4-1.26.91-1.9 1.55a57.69 57.69 0 0 1-4.31 3.86 147.88 147.88 0 0 1-3.06 2.44l-1 .8C137.01 582.43 134 587.18 134 597c0 1.02-.02 2.01-.07 3h-2c.05-.99.07-1.98.07-3 0-10.52 3.33-15.78 12.09-22.76a265.61 265.61 0 0 1 2-1.6c.83-.64 1.43-1.13 2.03-1.61a55.76 55.76 0 0 0 4.17-3.74c.74-.73 1.48-1.34 2.24-1.82 3.47-2.2 7-1.75 11.77.93 2.15 1.21 12.03 8 14.9 9.76a85.7 85.7 0 0 0 9.22 4.93C209.29 589.7 215.85 591 238 591c19.25 0 31.49-12.7 41.06-40.33 4.24-12.25 8.66-18.42 12.81-18.42 3.8 0 6.13 5.06 6.13 11.75 0 17.8-.63 28.8-3.3 41.7-.77 3.7-1.68 7.23-2.75 10.6-.4 1.3-.8 2.53-1.19 3.7zm-149.25 0 .5-.94a160.1 160.1 0 0 0 6.53-13.26c2.73-6.29 5.78-9.64 9.24-10.52 3.74-.95 7.15.74 12.56 5.13 5.43 4.4 6.07 4.86 7.73 5.1 1.6.22 4.28 1.14 8.86 2.95 1.3.5 10.78 4.35 13.85 5.55 3.07 1.2 5.85 2.25 8.49 3.18 3.1 1.1 5.98 2.04 8.65 2.81h-3.45c-1.76-.56-3.6-1.18-5.54-1.87a281.2 281.2 0 0 1-8.51-3.19c-3.08-1.2-12.57-5.04-13.86-5.55-4.5-1.78-7.15-2.68-8.63-2.9-1.94-.27-2.53-.7-8.22-5.3-5.17-4.2-8.36-5.78-11.69-4.94-3.1.78-5.94 3.92-8.56 9.95a161 161 0 0 1-6.82 13.8h-1.13zm112.89 0a30.34 30.34 0 0 0 11.27-6.27c1.55-1.36 3.32-3.46 5.34-6.29 1.05-1.46 2.15-3.1 3.41-5.04a349.73 349.73 0 0 0 2.5-3.9l.47-.75.93-1.47a89.17 89.17 0 0 1 3.25-4.86c1.05-1.43 1.82-2.23 2.44-2.46 1.02-.37 1.49.48 1.49 2.04l.01 2.11c.05 6.91-.08 11.32-.7 16.33a48.4 48.4 0 0 1-2.38 10.56h-1.07a46.47 46.47 0 0 0 2.45-10.68c.62-4.96.75-9.33.7-16.2l-.01-2.12c0-.97-.08-1.12-.15-1.1-.36.14-1.05.85-1.97 2.1a88.44 88.44 0 0 0-3.22 4.82l-.92 1.46-.48.75a1268.1 1268.1 0 0 1-2.5 3.92c-1.26 1.95-2.38 3.6-3.44 5.08-2.06 2.88-3.87 5.04-5.5 6.45a30.87 30.87 0 0 1-8.94 5.52h-2.98zm-183.72 0H69.3c3.37-3.43 5.19-8.33 5.19-15 0-18.6-.04-17.35 1.02-20.77.6-1.93 1.5-3.74 3.27-6.63.42-.7 4.92-7.8 6.78-10.86 3.04-4.97 11.04-16.5 12.21-18.56 3.48-6.08 4.72-12.06 4.72-24.18 0-7.85 2.5-14.2 8.1-23.44l2.84-4.63a72.67 72.67 0 0 0 2.49-4.4c1.62-3.15 2.48-5.78 2.62-8.28.2-3.78-1.3-7.29-4.9-10.9-5.13-5.12-8.6-5.43-11.2-1.85-2.12 2.92-3.48 7.74-5.06 16.47-.2 1.03-.82 4.6-.82 4.57-.83 4.67-1.4 7.33-2.1 9.6-1.35 4.42-3.7 7.61-8.36 12.26l-3.26 3.2c-6.38 6.39-9.68 11.51-11.36 19.5l-1.16 5.52c-.87 4.1-1.56 7.04-2.33 9.94-3.67 13.74-9.65 25.97-22.59 44.72-7.68 11.14-11.05 18.87-10.92 23.72h-1c-.12-5.16 3.35-13.05 11.1-24.28 12.87-18.67 18.8-30.8 22.44-44.42.77-2.88 1.45-5.8 2.32-9.89l1.16-5.51c1.73-8.22 5.13-13.5 11.64-20 .63-.64 2.84-2.8 3.25-3.21 4.57-4.54 6.82-7.62 8.12-11.84a81.58 81.58 0 0 0 2.07-9.48l.81-4.57c1.62-8.9 3-13.8 5.24-16.89 3-4.15 7.2-3.78 12.71 1.74 3.8 3.8 5.42 7.58 5.2 11.66-.15 2.66-1.05 5.41-2.73 8.68a73.6 73.6 0 0 1-2.52 4.46l-2.84 4.63c-5.52 9.1-7.96 15.3-7.96 22.92 0 12.28-1.28 18.43-4.85 24.68-1.2 2.1-9.21 13.65-12.22 18.58-1.87 3.06-6.37 10.18-6.78 10.86-1.73 2.82-2.6 4.57-3.17 6.4-1.02 3.28-.98 2.1-.98 20.48 0 6.52-1.7 11.44-4.82 15zM310.09 0h1.06c-.37.9-.77 1.83-1.2 2.82-3.9 9.06-5.45 15.15-5.45 25.18 0 7.64-2.1 11.6-6.64 13.05-3.46 1.1-5.72.98-17.57-.43-11.55-1.36-19.17-1.58-28.16-.14-6.24 2.49-25.91 7.02-32.13 7.02-11.15 0-36.76-2.88-54.12-7.01a22.08 22.08 0 0 0-16.95 2.48c-4.05 2.33-7.09 5.03-13.9 11.97-6.28 6.39-9.53 9.23-13.8 11.5-7.09 3.79-11.22 7.65-13.4 12.27-1.82 3.85-2.33 7.84-2.33 15.29 0 4.4-2.65 6.69-9.45 9.74.1-.05-2.97 1.31-3.84 1.71-8.78 4.06-12.71 8.29-12.71 16.55 0 12.52-4.86 19.22-17.34 27.96l-4.56 3.14c-1.9 1.3-3.3 2.3-4.67 3.3-.92.68-1.79 1.34-2.62 2-7.16 5.62-11 14.54-15.56 33.28-.63 2.57-3.3 14-4.07 17.14a350.44 350.44 0 0 1-5.2 19.33c-1.37 4.5-4.5 15.07-4.96 16.53-1.05 3.4-1.64 4.94-2.46 6.32-.82 1.4-6.85 9.08-12.64 18.27L0 277.98v-1.9l4.58-7.35a270.8 270.8 0 0 1 12.61-18.23c-.3.5 1.35-2.8 2.38-6.12.45-1.44 3.58-12.01 4.95-16.53 1.83-6.03 3.44-12.09 5.19-19.27.76-3.13 3.44-14.56 4.06-17.14 4.62-18.95 8.52-28.02 15.92-33.83.84-.67 1.72-1.33 2.65-2.01 1.38-1.02 2.8-2.01 4.7-3.32l4.54-3.14C73.83 140.57 78.5 134.13 78.5 122c0-8.74 4.2-13.26 13.29-17.45.88-.41 3.96-1.77 3.85-1.73 6.46-2.9 8.86-4.97 8.86-8.82 0-7.6.53-11.7 2.42-15.71 2.29-4.84 6.57-8.85 13.84-12.73 4.15-2.21 7.35-5 14.15-11.93 6.28-6.4 9.36-9.13 13.52-11.53a23.07 23.07 0 0 1 17.69-2.59c17.27 4.12 42.8 6.99 53.88 6.99 6.1 0 25.73-4.53 31.92-7 9.12-1.46 16.83-1.25 28.49.13 11.63 1.38 13.9 1.5 17.15.47 4.06-1.3 5.94-4.85 5.94-12.1 0-10.1 1.56-16.3 6.6-28zm25.12 0h1c.05 5.62.26 11.48.65 19.4.47 9.7.64 14.57.64 21.6 0 9.81-4.68 17.46-13.1 23.16-6.53 4.43-14.94 7.46-24.33 9.33-3.74.54-9.42.56-22.68.23-6.74-.17-9.35-.22-12.39-.22-2.77 0-4.97.43-7.63 1.36-.88.3-4.55 1.74-5.58 2.11-6.55 2.35-13.59 3.53-24.79 3.53-8.1 0-13.58-1.38-22.46-4.9l-3.18-1.25c-12.55-4.87-21.27-5.15-37.18 1.12-11.15 4.39-18.13 9.2-22.28 14.81-3.15 4.26-4.33 7.8-5.94 15.8-1.22 6.09-1.93 8.74-3.5 12.13-1.65 3.53-3.97 5.81-7.07 7.22-2.33 1.07-4.35 1.5-9.32 2.19-9.04 1.27-12.77 3.09-15.61 9.58-3.71 8.48-7.72 13.87-14.22 19.76-2.4 2.18-13.14 11.02-15.91 13.42-8.2 7.1-13.85 17.37-18.7 31.97a258.81 258.81 0 0 0-3.27 10.7c-.01.05-2.26 7.97-2.88 10.1-8.49 28.85-17.88 52.95-26.13 61.2-2.8 2.8-5.06 5.64-10.4 12.96-3.4 4.68-6.23 8.25-8.95 11.1v-1.55c2.74-2.98 5.73-6.82 9.48-11.97 4.03-5.52 6.32-8.4 9.17-11.24 8.07-8.08 17.44-32.14 25.87-60.8.62-2.1 2.86-10.03 2.88-10.08 1.21-4.24 2.21-7.53 3.28-10.74 4.9-14.75 10.63-25.16 19-32.4 2.78-2.42 13.5-11.25 15.89-13.4 6.4-5.8 10.32-11.09 13.97-19.43 1.68-3.83 4.05-6.31 7.2-7.86 2.4-1.17 4.64-1.67 9.53-2.36 4.54-.63 6.5-1.05 8.7-2.06 2.89-1.31 5.03-3.42 6.58-6.73 1.53-3.3 2.23-5.9 3.43-11.9 1.64-8.14 2.85-11.79 6.11-16.2 4.28-5.79 11.41-10.7 22.73-15.16 16.15-6.36 25.13-6.07 37.9-1.11l3.19 1.26c8.77 3.47 14.13 4.82 22.09 4.82 11.09 0 18.02-1.16 24.46-3.47 1-.36 4.68-1.8 5.58-2.11A22.5 22.5 0 0 1 265 72.5c3.05 0 5.67.05 14.07.26 11.53.29 17.2.27 20.83-.25 9.25-1.85 17.54-4.83 23.94-9.17C332 57.8 336.5 50.46 336.5 41c0-7-.17-11.86-.7-22.7-.35-7.26-.55-12.83-.59-18.3zM93.87 0h2.04c-.7 4-1.61 6.82-3.03 9.47-2.33 4.38-2.85 5.75-5.26 13.03a40.46 40.46 0 0 1-1.94 5.03c-2.24 4.66-5.92 8.8-13.07 14.26-8.01 6.13-14.27 16.55-20.03 31.55-2.4 6.23-8.75 25.63-9.64 28.01-2.69 7.16-6.56 12.7-15.63 23.68l-2.68 3.24c-6.02 7.34-9.35 12.07-11.72 17.15-2.3 4.94-7.12 9.9-12.91 14.15v-2.4c5.14-3.94 9.1-8.3 11.1-12.6 2.46-5.27 5.87-10.1 11.98-17.56l2.68-3.26c8.94-10.8 12.72-16.22 15.3-23.1.88-2.33 7.24-21.74 9.65-28.03 5.89-15.31 12.3-26 20.68-32.41 6.92-5.3 10.4-9.2 12.48-13.55.65-1.35 1.16-2.7 1.85-4.79 2.45-7.4 3-8.83 5.4-13.34A27.68 27.68 0 0 0 93.87 0zm9.07 0h1.02c-1.66 8.3-2.91 12.67-4.54 15.26a59.14 59.14 0 0 0-4.1 8.21c-1.27 3-2.44 6.2-3.5 9.4-.38 1.12-.7 2.16-2.41 5.39a251.48 251.48 0 0 0-12.81 13.3c-3.48 3.96-5.95 7.27-7.15 9.66-.95 1.9-2.06 5.99-3.61 12.97-.64 2.9-3.65 17.15-4.51 21.07-3.63 16.45-6.63 26.69-9.9 32-7.66 12.45-10.64 15.71-37.08 41.1A69.78 69.78 0 0 1 0 179.21v-1.15a69.39 69.39 0 0 0 13.65-10.42c26.4-25.33 29.32-28.55 36.92-40.9 3.2-5.18 6.18-15.37 9.78-31.7.86-3.91 3.87-18.16 4.51-21.06 1.57-7.09 2.7-11.2 3.7-13.2 1.24-2.5 3.76-5.86 7.29-9.89.9-1.03 1.86-2.1 2.86-3.18 2.4-2.6 4.96-5.22 7.53-7.76.9-.88 1.73-1.7 3.37-3.4a129.02 129.02 0 0 1 4.78-13.46 60.07 60.07 0 0 1 4.19-8.35c1.52-2.44 2.74-6.71 4.36-14.74zM83.71 0h1.1c-2.09 4.74-6.03 8.92-11.42 12.3-7.2 4.52-16.5 7.2-24.39 7.2-8.9 0-11.8 7-11.74 21.52 0 1.7.04 3.17.12 5.99.1 3.3.12 4.45.12 5.99 0 5.73-.76 11.3-2.01 16.5a66.67 66.67 0 0 1-2.15 6.97 2597.76 2597.76 0 0 1-7 15.86 4270.8 4270.8 0 0 1-19.9 43.87A54.64 54.64 0 0 1 0 147v-1.65a54.87 54.87 0 0 0 5.55-9.57A4269.82 4269.82 0 0 0 30.7 79.97c.53-1.2.99-2.23 2.44-5.9A69.23 69.23 0 0 0 36.5 53c0-1.52-.03-2.66-.12-5.95-.08-2.83-.12-4.31-.12-6.01-.03-6.79.53-11.62 2.07-15.34 1.94-4.68 5.39-7.19 10.67-7.19 7.7 0 16.81-2.63 23.86-7.05C77.93 8.27 81.66 4.38 83.7 0zm282.63 0h1.01c1.86 10.02 2.18 12.67 2.32 18.3a123.43 123.43 0 0 1 .37 27.83c-.96 8.78-3.1 16.01-6.63 21.15-11.34 16.5-39.8 29.22-66.41 29.22-5.09 0-10.47.28-16.31.83a413.8 413.8 0 0 0-24.37 3.16c-21.56 3.26-27.66 4.01-36.32 4.01-6.92 0-12.2-1.05-21.69-3.9l-2.78-.83c-1.39-.41-2.54-.74-3.65-1.02-8-2.05-14.22-2.04-21.7.72a16.32 16.32 0 0 0-9.17 8.18c-1.6 3.05-2.5 6.06-4.02 12.83-1.5 6.64-2.34 9.52-3.99 12.64a16.16 16.16 0 0 1-9.85 8.36 104.8 104.8 0 0 0-9.5 3.42c-6.55 2.8-10.1 5.57-13.8 10.47-1.33 1.75-1.03 1.3-5.43 7.9-1.98 2.97-4.66 5.8-8.48 9.14-2.01 1.76-10.71 8.83-12.88 10.7-7.37 6.35-12.58 12.14-16.63 19.14-4.22 7.3-7.8 18.3-11.28 33.26-.87 3.73-1.72 7.64-2.64 12.14l-1.18 5.8-1.09 5.45c-1.8 8.96-2.77 13.28-3.77 16.26-6.8 20.44-17.26 42.16-27.13 51.2-5.11 4.7-8.1 7.07-11.1 8.86-.9.54-1.84 1.04-2.92 1.57-.44.22-9.6 4.4-14.1 6.66l-1.22.62v-1.13l.78-.39c4.52-2.26 13.67-6.44 14.1-6.65a41.19 41.19 0 0 0 2.84-1.54c2.94-1.75 5.88-4.09 10.94-8.73 9.71-8.9 20.1-30.51 26.87-50.79.97-2.92 1.94-7.22 3.73-16.13l1.1-5.46a490.5 490.5 0 0 1 3.82-17.96c3.5-15.06 7.1-26.14 11.39-33.54 4.11-7.11 9.4-12.98 16.83-19.4 2.19-1.88 10.88-8.95 12.88-10.7 3.77-3.28 6.39-6.05 8.3-8.93 4.43-6.64 4.12-6.18 5.47-7.96 3.8-5.03 7.5-7.91 14.21-10.78 2.61-1.12 5.74-2.24 9.59-3.46a15.17 15.17 0 0 0 9.27-7.86c1.59-3.02 2.42-5.85 4.03-12.99 1.41-6.27 2.32-9.33 3.98-12.48a17.31 17.31 0 0 1 9.7-8.66c7.7-2.83 14.1-2.84 22.3-.75 1.12.29 2.28.61 3.68 1.03l3.73 1.11c8.47 2.54 13.66 3.58 20.46 3.58 8.59 0 14.67-.75 36.18-4a414.64 414.64 0 0 1 24.41-3.17c5.88-.54 11.29-.83 16.41-.83 26.3 0 54.45-12.58 65.59-28.78 3.42-4.98 5.5-12.06 6.46-20.7.84-7.74.73-16.02.02-23.9a136.2 136.2 0 0 0-.57-5.12c0-4.47-.3-6.94-2.16-17zM18.88 0h1.03C18 7.57 17.15 10.18 14.46 16.2c-1.95 4.37-2.67 9.19-2.42 14.89.2 4.33.71 7.7 2.28 16.13 1.09 5.88 1.57 8.77 1.94 12.2.96 8.9.24 16.08-2.8 22.79A463.4 463.4 0 0 1 0 109.43v-2.12a465 465 0 0 0 12.54-25.52c2.97-6.52 3.67-13.53 2.72-22.27-.36-3.4-.84-6.26-1.93-12.12-1.57-8.47-2.1-11.88-2.29-16.27-.26-5.84.48-10.81 2.5-15.33 2.64-5.9 3.48-8.47 5.34-15.8zm280.47 0a70.78 70.78 0 0 1-4.91 11.24c-2.56 4.7-4.01 8.45-4.86 11.98l-.4 1.8-.28 1.45a5.28 5.28 0 0 1-.74 2.07c-.74 1.03-1.93 1.28-5.13 1.25.92 0-9.85-.29-15.03-.29-10.2 0-18.45.82-29.46 2.56-16.87 2.66-17.73 2.77-23.66 2.52a42.57 42.57 0 0 1-8-1.09c-17.7-4.16-46.18-5.86-54.72-3.01-2.72.9-5.88 2.8-9.52 5.59a112.37 112.37 0 0 0-6.54 5.48c-1.4 1.25-9.17 8.5-10.78 9.84-1.45 1.2-8.18 7.42-8.85 8.02a114.65 114.65 0 0 1-4.55 3.9c-4.99 4.03-8.9 6.2-11.92 6.2-3.52.05-4.32 0-5.14-.4-1.13-.56-1.5-1.72-1.13-3.57.74-3.63 4.47-10.84 12.84-24.8 5.69-9.48 9.42-18 11.78-26.2 1.45-5.04 1.94-7.4 2.97-14.54h1.01c-1.05 7.3-1.54 9.7-3.01 14.82-2.39 8.28-6.16 16.89-11.9 26.44-8.3 13.84-12 21.01-12.7 24.48-.3 1.45-.08 2.14.59 2.47.6.3 1.35.35 3.48.3 3.92 0 7.69-2.1 12.5-5.98a114.6 114.6 0 0 0 4.51-3.86c.66-.59 7.41-6.83 8.88-8.05 1.59-1.33 9.34-8.55 10.75-9.82 2.4-2.15 4.55-3.96 6.6-5.53 3.72-2.85 6.97-4.8 9.81-5.74 8.76-2.92 37.41-1.22 55.27 2.99 2.57.6 5.14.95 7.81 1.06 5.84.25 6.7.14 23.47-2.51 11.05-1.75 19.36-2.57 29.6-2.57 5.2 0 15.99.3 15.05.29 2.87.03 3.84-.17 4.3-.83.23-.32.4-.8.58-1.7l.28-1.43.4-1.85c.88-3.6 2.36-7.44 4.96-12.22A69.5 69.5 0 0 0 298.29 0h1.06zm-8.59 0c-5.91 17.94-9.55 22-19.76 22-4.5 0-10.22.32-28.69 1.5l-1.53.1c-15.6.99-23.47 1.4-28.78 1.4-5.35 0-13.24-.96-28.86-3.28l-1.54-.23C163.18 18.75 157.47 18 153 18c-4.45 0-7.3 1.01-10.96 3.34-.1.06-1.8 1.17-2.3 1.47-2.43 1.5-4.32 2.19-6.74 2.19-2.8 0-4.11-1.46-4.11-4.22 0-1.04.16-2.29.5-4.1.16-.82.9-4.4 1.07-5.32.8-4.11 1.3-7.68 1.47-11.36h2c-.17 3.82-.68 7.5-1.5 11.75-.19.94-.92 4.5-1.07 5.31a21.04 21.04 0 0 0-.47 3.72c0 1.7.46 2.22 2.11 2.22 1.99 0 3.55-.57 5.7-1.9.47-.28 2.15-1.37 2.26-1.44C144.92 17.14 148.12 16 153 16c4.62 0 10.3.74 28.9 3.51l1.53.23C198.93 22.04 206.8 23 212 23c5.25 0 13.11-.41 28.65-1.4l1.54-.1C260.73 20.32 266.43 20 271 20c8.95 0 12.15-3.4 17.66-20h2.1zM141.51 0h1.13c-2.06 3.86-2.63 5.1-2.77 6.19-.15 1.12.42 1.64 2.32 1.96 1.8.3 3.85.35 10.81.35 6.02 0 13 .56 21.35 1.62 3.95.5 8.03 1.1 13.13 1.89 24 3.7 22.5 3.49 26.83 3.49 24.02 0 51.83-2.24 60.45-6.94 2.88-1.57 5.05-4.49 6.6-8.56h1.07c-1.64 4.47-3.98 7.69-7.2 9.44-8.83 4.82-36.67 7.06-60.92 7.06-4.41 0-2.84.22-26.98-3.5-5.1-.8-9.17-1.38-13.1-1.88-8.31-1.06-15.26-1.62-21.23-1.62-7.04 0-9.1-.05-10.97-.37-2.38-.4-3.38-1.32-3.15-3.07.16-1.22.69-2.41 2.63-6.06zm76.4 0c5.69 1.64 10.37 2.5 14.09 2.5 9.59 0 16.7-.71 22.4-2.5h2.98C251.12 2.53 243.2 3.5 232 3.5c-4.5 0-10.32-1.21-17.53-3.5h3.45zM70.69 0c-2.87 3.27-6.95 5.39-12.02 6.53-3.98.89-7.5 1.08-12.92 1A97.24 97.24 0 0 0 44 7.5c-5.37 0-8.86-1.24-10.1-4.97A8.6 8.6 0 0 1 33.5 0h.99c.02.82.14 1.56.36 2.22C35.91 5.39 39.02 6.5 44 6.5l1.76.02c5.35.09 8.8-.1 12.69-.97C62.95 4.54 66.63 2.74 69.3 0h1.37zM0 207.87c7.31-.16 11.5 3.33 11.5 11.13 0 11.41-5.05 28.35-11.5 41.5v-2.3c5.93-12.72 10.5-28.47 10.5-39.2 0-7.18-3.7-10.3-10.5-10.13v-1zm0 7.05c1.23.14 2.18.58 2.87 1.31 1.4 1.48 1.6 3.72 1.16 7.58l-.16 1.3A28.93 28.93 0 0 0 3.5 229c0 3.2-1.48 9.52-3.5 15.9v-3.45c1.49-5.13 2.5-9.87 2.5-12.45 0-.98.08-1.75.37-4.02l.16-1.29c.42-3.56.24-5.59-.88-6.77-.5-.53-1.21-.87-2.15-1v-1zM0 410.9v-1.47a21.67 21.67 0 0 0 2.97-4.7c1.32-2.7 2.68-6.28 4.56-11.89 7.85-23.55 7.83-26.6.25-30.4-2.25-1.12-4.8-1.43-7.78-.91v-1.02a13.1 13.1 0 0 1 8.22 1.04c8.24 4.12 8.26 7.6.25 31.6-1.88 5.66-3.25 9.27-4.6 12.02A20.82 20.82 0 0 1 0 410.9zM33.64 452c1.68 0 3.04-.23 8.34-1.31l2.38-.47c8.26-1.57 12.72-1.3 14.53 2.33 1.38 2.75-.47 5.86-4.75 9.68a75.6 75.6 0 0 1-5.08 4.07c-.94.7-4.89 3.59-5.79 4.27-1.86 1.4-2.97 2.37-3.47 3.03a19.08 19.08 0 0 0-2.89 5.5c.07-.2-4.02 13.65-6.96 22.22-2.7 7.85-5.56 10.72-8.82 8.59-2.11-1.4-3.66-4.24-6.6-11.03-1.98-4.62-2.5-5.76-3.4-7.4-4.55-8.18-3.9-23.9-.05-32.87a9.6 9.6 0 0 1 6.98-5.96c2.59-.66 4.86-.75 11.78-.67l3.8.02zm0 2c-1.13 0-2.09 0-3.82-.02-12.07-.13-14.83.57-16.9 5.41-3.63 8.47-4.26 23.55-.05 31.12.96 1.73 1.48 2.88 3.5 7.58 2.72 6.3 4.24 9.08 5.86 10.14 1.64 1.08 3.5-.8 5.82-7.55a682.9 682.9 0 0 0 6.97-22.24 21.03 21.03 0 0 1 3.18-6.04c.65-.87 1.85-1.9 3.86-3.43.92-.7 4.87-3.57 5.8-4.27 2.02-1.5 3.6-2.77 4.95-3.97 3.63-3.23 5.09-5.7 4.3-7.28-1.21-2.42-5.07-2.65-12.38-1.27l-2.35.47c-5.49 1.11-6.86 1.35-8.74 1.35zm345.63 146c-3.45-12.26-3.77-14.13-3.77-19 0-3.33-.13-6.27-.43-11.34-.63-10.33-.65-13.5.26-17.07 1.21-4.74 4.21-7.1 9.67-7.1h26c4.08 0 5.19 1.85 5.93 7.11.1.79.13.97.19 1.32.84 5.35 2.8 7.58 8.88 7.58 3.64 0 5.54.4 6.43 1.37.76.83.76 1.44.36 3.93-.85 5.26.5 8.85 7.5 13.8 6.32 4.45 11.63 5.36 16.55 3.37 3.8-1.54 6.73-4.16 11.92-10l1.1-1.23 1.09-1.23a75.6 75.6 0 0 1 2.7-2.86 35.81 35.81 0 0 1 9.57-6.73c1.52-.76 1.72-.86 5.66-2.63 6.1-2.73 9.01-4.5 11.74-7.62 2.63-3 4.67-4.85 6.7-6.04 3.18-1.85 5.46-2.13 13.68-2.13 5.98 0 10.56-4.32 18-14.99l2.82-4.03c1.06-1.5 1.94-2.7 2.79-3.79 7.87-10.12 19.38-10.4 30.74.96 5.54 5.53 10.17 19.43 13.64 38.51 2.5 13.75 4.18 29.46 4.47 39.84h-1c-.3-10.32-1.96-25.97-4.45-39.66-3.43-18.87-8.02-32.65-13.36-37.99-10.95-10.95-21.76-10.68-29.26-1.04-.83 1.07-1.7 2.26-2.75 3.75l-2.81 4.02c-7.65 10.95-12.38 15.42-18.83 15.42-8.04 0-10.21.26-13.17 2-1.92 1.12-3.9 2.9-6.45 5.83-2.86 3.26-5.87 5.09-12.09 7.88a103.35 103.35 0 0 0-5.62 2.6 34.84 34.84 0 0 0-9.32 6.54 74.67 74.67 0 0 0-3.75 4.05l-1.1 1.24c-5.28 5.95-8.29 8.64-12.28 10.25-5.26 2.13-10.92 1.17-17.5-3.48-7.33-5.17-8.82-9.15-7.92-14.77.34-2.12.34-2.6-.1-3.1-.64-.69-2.34-1.04-5.7-1.04-6.63 0-8.96-2.63-9.87-8.42l-.2-1.34c-.67-4.82-1.53-6.24-4.93-6.24h-26c-5 0-7.6 2.04-8.7 6.34-.88 3.43-.85 6.57-.23 16.76a177 177 0 0 1 .43 11.4c0 4.78.32 6.63 3.81 19h-1.04zm13.68 0c-1.31-6.58-1.61-10.71-1.36-14.84.04-.7.1-1.44.18-2.38l.23-2.56c.34-3.81.5-6.97.5-11.22 0-4.94 1.46-7.76 4.21-8.42 2.38-.58 5.56.54 9.2 3 6.64 4.52 13.99 13.07 16.55 19.23 4.77 11.44 14.12 15.69 33.54 15.69 8.6 0 14.32-2.35 20.67-7.88 1.45-1.26 15.06-15 21-20 7.21-6.07 11.77-7.59 20.62-8.32 5.52-.45 7.98-.9 11.44-2.36 4.58-1.95 9.36-5.48 14.9-11.29 7.43-7.76 13.25-8.92 17.47-4.3 3.32 3.63 5.46 10.58 6.82 20.24.73 5.17.94 7.74 1.58 17.38.25 3.75.17 5.32-.92 18.03h-1c1.09-12.7 1.17-14.28.92-17.97-.64-9.6-.85-12.16-1.57-17.3-1.33-9.47-3.43-16.27-6.56-19.7-3.76-4.11-8.93-3.08-16 4.32-5.65 5.9-10.54 9.5-15.25 11.5-3.58 1.53-6.13 1.99-11.6 2.44-8.8.72-13.17 2.18-20.2 8.1-5.9 4.96-19.5 18.7-21 19.99-6.52 5.68-12.47 8.12-21.32 8.12-19.78 0-29.5-4.42-34.46-16.3-2.49-5.97-9.71-14.38-16.2-18.79-3.42-2.32-6.36-3.35-8.4-2.86-2.2.53-3.44 2.92-3.44 7.45 0 4.28-.16 7.47-.5 11.31l-.23 2.56c-.09.93-.14 1.65-.19 2.35-.24 4.08.06 8.18 1.39 14.78h-1.02zm113.75 0c2.52-3.26 8.93-11.79 10.9-14.3 5.48-6.98 13.05-12.38 19.4-13.94 7.01-1.71 11.5 1.45 11.5 9.24 0 4.02-.04 5.16-.74 19h-1c.7-13.85.74-15 .74-19 0-7.12-3.86-9.83-10.26-8.26-6.11 1.5-13.5 6.77-18.85 13.57-1.86 2.36-7.65 10.07-10.43 13.69h-1.26zm-9.86-338.96c3.44 2.71 7 5.1 11.44 7.75 1.06.64 8.42 4.9 10.35 6.1 11.27 7 15 13.35 12.35 25.33-1.45 6.52-4.53 11.1-9.39 14.44-3.83 2.63-8.07 4.26-16.08 6.56-11.97 3.45-13.68 3.99-18.82 6.28a60.18 60.18 0 0 0-7.81 4.18c-11.11 7.07-19.1 7.7-27.96 3.28-3.56-1.77-17.2-11-17.2-11.01a101.77 101.77 0 0 0-5.2-3.07c-16.04-8.83-34.27-24.16-34.52-31.85-.11-3.46 1.99-6.57 6.28-10.26 1.03-.9 2.18-1.81 3.68-2.95.72-.55 3.38-2.56 3.94-3 4.47-3.4 7.18-5.79 9.32-8.45 11.12-13.82 26.55-28.68 34.36-32.28 12.06-5.54 19.84-5.77 27.37.12 3.25 2.54 5.65 6.54 8.58 13.35.29.65 2.3 5.45 2.88 6.74 1.62 3.65 2.9 5.8 4.24 6.94.72.6 1.45 1.2 2.2 1.8zm-3.49-.28c-1.63-1.39-3.03-3.74-4.77-7.65-.58-1.3-2.6-6.12-2.88-6.76-2.81-6.5-5.08-10.3-7.98-12.56-6.83-5.35-13.85-5.15-25.3.12-7.45 3.42-22.7 18.12-33.64 31.72-2.27 2.82-5.08 5.3-9.67 8.79l-3.94 2.98a79.98 79.98 0 0 0-3.59 2.88c-3.87 3.33-5.67 6-5.58 8.69.21 6.64 18.14 21.72 33.48 30.15 1.76.97 3.5 2 5.3 3.13.12.08 13.61 9.22 17.03 10.92 8.22 4.1 15.46 3.52 26-3.18a62.17 62.17 0 0 1 8.07-4.31c5.25-2.35 7-2.9 19.08-6.38 7.8-2.24 11.9-3.82 15.5-6.3 4.44-3.04 7.23-7.18 8.56-13.22 2.44-11.02-.83-16.6-11.45-23.2-1.9-1.18-9.23-5.42-10.32-6.08-4.5-2.69-8.13-5.12-11.64-7.9-.77-.6-1.52-1.21-2.26-1.84zM87.72 241.6c4.3-2.98 7.88-5 12.14-6.95.84-.4 1.73-.78 2.78-1.24l4.37-1.88a164.3 164.3 0 0 0 17.74-8.96 320.67 320.67 0 0 1 27.87-14.5c4.22-1.95 21.89-9.84 21.17-9.52 19.17-8.62 28.1-6.93 49.5 8.05 7.91 5.54 13.24 13.25 16.45 22.66 3.02 8.83 3.76 16.51 3.76 27.75 0 8.32-.66 12.95-3.68 18.97-4.18 8.36-12.3 16.14-25.58 23.47-24.45 13.49-38.83 27.55-52.83 47.84-8.83 12.8-47.76 44.21-65.16 54.15C75.04 413.55 48.89 423.5 31 423.5c-10.05 0-14.67-4.78-14.76-13.37-.07-6.32 2.06-13.73 6.3-24.32 2.95-7.37 2.02-12.9-2.16-22.29-3.19-7.17-3.88-9.14-3.88-12.52 0-3.35 1.87-6.9 5.52-11.07 2.61-3 3.5-3.83 11.9-11.5 5.09-4.66 8.08-7.6 10.7-10.75 9.46-11.36 12.62-19.47 17.9-44.78 3.12-15.05 6.63-20.28 15.12-25.25.8-.47 3.95-2.25 4.7-2.68a76.66 76.66 0 0 0 5.38-3.38zm.56.82a77.63 77.63 0 0 1-5.44 3.43l-4.7 2.67c-8.23 4.82-11.57 9.81-14.65 24.6-5.3 25.45-8.51 33.7-18.1 45.21-2.66 3.19-5.68 6.16-10.8 10.84-8.36 7.64-9.24 8.48-11.82 11.42-3.5 4.01-5.27 7.36-5.27 10.42 0 3.18.68 5.1 3.8 12.12 4.27 9.6 5.24 15.37 2.16 23.07-4.18 10.47-6.29 17.78-6.22 23.93.08 8.06 4.26 12.38 13.76 12.38 17.67 0 43.68-9.9 64.75-21.93 17.28-9.88 56.1-41.2 64.84-53.85 14.08-20.42 28.57-34.59 53.17-48.16 13.12-7.23 21.09-14.87 25.17-23.03 2.92-5.86 3.57-10.35 3.57-18.53 0-11.13-.74-18.73-3.7-27.43-3.15-9.22-8.36-16.75-16.09-22.16-21.13-14.8-29.7-16.42-48.5-7.95.7-.32-16.96 7.56-21.17 9.5-1.7.8-3.3 1.55-4.86 2.3a319.68 319.68 0 0 0-22.93 12.17 165.3 165.3 0 0 1-17.85 9.01l-4.37 1.88c-1.04.45-1.92.84-2.76 1.23a74.56 74.56 0 0 0-11.99 6.86zm-7.6 12.2c7.7-6.25 12.3-8.17 23.68-11.27 6.12-1.67 9.12-2.95 12.31-5.72 3.8-3.3 7.47-4.52 15.86-6.1 2.75-.52 3.67-.7 5.06-1.02 5.48-1.24 9.48-2.93 13.1-5.89 10.42-8.53 25.4-14.11 36.31-14.11 5.33 0 16.77 7.58 25.74 17.16 10.73 11.46 15.96 23.27 12.73 32.5-3.18 9.1-11.39 18.57-23.03 27.86-8.44 6.73-18.36 13-25.22 16.43-3.72 1.86-6.59 4.88-9.77 9.99-.69 1.1-11.1 20.25-16.03 27.83-5.62 8.65-15.4 17.36-30.23 27.96a552.58 552.58 0 0 1-9.2 6.42c-.13.09-6.81 4.65-8.6 5.89-6.47 4.46-10.35 7.35-13.05 9.83-11.64 10.67-37.14 15.54-43.7 8.98-1.96-1.96-2.2-4.06-1.95-10.52.37-9.42-.5-14.5-4.95-20.51a34.09 34.09 0 0 0-7.04-6.92c-3.93-2.95-6.07-6.11-6.56-9.49-.97-6.61 3.87-13.06 14.17-21.69 1.58-1.32 6.67-5.44 7.09-5.78a48.03 48.03 0 0 0 5.23-4.77c4.1-4.63 5.85-9.55 7.8-20.07a501.52 501.52 0 0 0 .8-4.37c.33-1.87.6-3.3.88-4.73.74-3.78 1.5-7.18 2.4-10.63 1-3.78 1.38-5.5 2.36-10.37.6-3.02.93-4.21 1.56-5.47 1.22-2.45 1.27-2.5 12.25-11.42zm.64.78c-10.77 8.74-10.88 8.84-12 11.08-.58 1.16-.88 2.3-1.47 5.22-.98 4.89-1.36 6.63-2.37 10.44-.9 3.43-1.65 6.8-2.39 10.56a339.79 339.79 0 0 0-1.29 6.95l-.39 2.15c-1.98 10.68-3.77 15.74-8.04 20.54a48.77 48.77 0 0 1-5.34 4.88c-.42.34-5.5 4.47-7.07 5.78-10.04 8.4-14.72 14.65-13.83 20.78.45 3.1 2.44 6.03 6.17 8.83 3 2.25 5.39 4.62 7.24 7.12 4.63 6.24 5.52 11.52 5.15 21.15-.25 6.14-.01 8.1 1.66 9.78 6.1 6.1 31.02 1.33 42.31-9.02 2.75-2.52 6.66-5.43 13.16-9.92l8.6-5.89c3.63-2.48 6.45-4.44 9.19-6.4 14.73-10.54 24.44-19.18 29.97-27.7 4.9-7.54 15.31-26.68 16.02-27.8 3.27-5.26 6.26-8.41 10.18-10.37 6.79-3.4 16.65-9.63 25.03-16.32 11.52-9.18 19.61-18.53 22.72-27.4 3.07-8.78-2.02-20.27-12.52-31.49-8.8-9.4-20.04-16.84-25.01-16.84-10.67 0-25.43 5.5-35.68 13.89-3.76 3.07-7.9 4.81-13.5 6.09-1.41.32-2.35.5-5.11 1.02-8.21 1.55-11.76 2.73-15.38 5.88-3.34 2.9-6.45 4.22-12.7 5.92-11.26 3.07-15.75 4.94-23.31 11.09zM212 251.85c0 7.56-.6 10.92-2.6 14.3-1.1 1.84-7.66 10.05-8.6 11.3-5.96 7.94-9.33 10.28-17.26 13.76-1.34.58-2.2 1-3.03 1.5-.55.33-1.2.66-2 1.02-.71.33-4.46 1.9-5.52 2.39-6.05 2.78-8.99 5.8-8.99 10.73 0 10.97-18.95 36.12-34.51 44.87-8.18 4.6-21.3 9.36-32.78 11.86-13.33 2.9-22.49 2.48-24.62-2.32-1.32-2.97-4.4-4.26-11.98-5.81l-.6-.12c-4.84-.99-6.94-1.55-9.03-2.64-2.92-1.5-4.48-3.7-4.48-6.84 0-2.74 1.08-5.77 3.25-9.67.85-1.53 1.82-3.13 3.23-5.35-.16.25 2.83-4.4 3.67-5.76 6.69-10.7 9.85-18.5 9.85-27.22 0-18.41 11.22-33.37 27.5-42.86 5.22-3.05 9.23-3.31 15.2-2.12 5.04 1 6.05.9 7.43-1.52 4.5-7.85 7.04-9.5 15.87-9.5 3.93 0 6.97-.98 10.47-3.16 1.56-.97 8.67-6.17 10.99-7.68 9.2-5.98 11.34-7 25.2-11.95 6.95-2.48 15.18 1.28 22.33 9.12 6.55 7.19 11.01 16.61 11.01 23.67zm-2 0c0-6.5-4.25-15.48-10.49-22.32-6.67-7.32-14.16-10.74-20.17-8.59-13.73 4.9-15.73 5.85-24.8 11.75-2.24 1.46-9.37 6.68-11.01 7.7-3.8 2.36-7.2 3.46-11.53 3.46-8.08 0-9.98 1.23-14.13 8.5-1.1 1.91-2.51 2.88-4.35 3.09-1.3.14-1.9.05-5.22-.61-5.53-1.1-9.07-.88-13.8 1.88-15.72 9.17-26.5 23.55-26.5 41.14 0 9.2-3.28 17.29-10.15 28.28l-3.68 5.77c-1.39 2.19-2.35 3.77-3.17 5.25-2.02 3.63-3 6.38-3 8.7 0 4.19 2.87 5.67 11.9 7.52l.61.12c8.27 1.7 11.7 3.13 13.4 6.95 3.17 7.14 36 0 54.6-10.46 14.98-8.43 33.49-32.99 33.49-43.13 0-5.9 3.47-9.48 10.16-12.55 1.1-.5 4.85-2.08 5.52-2.38.74-.34 1.32-.64 1.8-.93.92-.55 1.85-1 3.25-1.62 7.65-3.35 10.75-5.5 16.47-13.12 1.02-1.36 7.47-9.42 8.47-11.11 1.79-3.01 2.33-6.06 2.33-13.3zm-37.18-22.4c.15-.1 2.4-1.51 2.95-1.84.96-.57 1.7-.94 2.43-1.17 2.57-.83 5.06-.1 11.04 3.12 14.86 8 19.43 22.87 9.18 38.71-4.04 6.24-9.37 9-18.72 11.11-.85.2-1.2.27-3.13.68-6.04 1.29-8.78 2.08-11.6 3.65-3.63 2.02-6.09 4.98-7.5 9.44-7.87 24.93-19.72 43.34-36.28 50.31-16.45 6.93-21.13 8.53-27.98 8.89-4.94.25-9.8-.65-15.4-2.89a44.45 44.45 0 0 1-5.64-2.6c-4.02-2.33-5.14-4.74-4.5-9.31.3-2.13 3.77-15.53 4.84-20.65.63-3.05 1.19-6.14 1.75-9.69a464.04 464.04 0 0 0 1.35-8.9c1.42-9.41 2.5-14.27 4.49-18.65 2.46-5.43 6.13-9.03 11.72-11.13 6.59-2.47 10.54-3.1 18.03-3.53 4.75-.27 6.68-.64 9-2.05.61-.37 1.22-.81 1.82-1.33a30.61 30.61 0 0 0 3.37-3.4c.59-.69 2.38-2.9 2.63-3.19 3.36-4 6.3-5.53 12.33-5.53 3.94 0 5.9-.92 8.18-3.36-.17.18 2.75-3.14 3.85-4.22a30.95 30.95 0 0 1 6.79-5c1.5-.83 3.15-1.62 4.99-2.38a64.92 64.92 0 0 0 10.01-5.1zm-14.52 8.34a29.95 29.95 0 0 0-6.57 4.84 116.68 116.68 0 0 0-3.82 4.2c-2.46 2.63-4.68 3.67-8.91 3.67-5.72 0-8.39 1.39-11.57 5.17-.23.28-2.03 2.5-2.63 3.2a31.6 31.6 0 0 1-3.47 3.51c-.65.55-1.3 1.03-1.96 1.43-2.5 1.51-4.55 1.9-9.47 2.19-7.39.42-11.25 1.04-17.72 3.47-5.34 2-8.82 5.4-11.17 10.6-1.93 4.27-3 9.07-4.41 18.39l-.65 4.34-.7 4.57c-.57 3.56-1.12 6.67-1.76 9.73-1.08 5.18-4.54 18.53-4.83 20.59-.59 4.17.35 6.18 4.01 8.3 1.35.77 3.1 1.58 5.52 2.55 5.46 2.18 10.18 3.05 14.97 2.8 6.69-.34 11.32-1.93 27.65-8.8 16.21-6.83 27.92-25.01 35.71-49.7 1.49-4.7 4.12-7.86 7.97-10 2.93-1.63 5.74-2.45 11.87-3.76 1.92-.4 2.28-.49 3.12-.68 9.12-2.06 14.24-4.7 18.1-10.67 9.92-15.34 5.55-29.55-8.82-37.29-5.75-3.1-8.03-3.76-10.25-3.05-.65.2-1.33.54-2.23 1.08-.55.32-2.77 1.72-2.93 1.82a65.91 65.91 0 0 1-10.16 5.17c-1.8.75-3.42 1.52-4.89 2.33zm-42.39 32.72c16.15-2.87 26.36-.97 32.47 6.16 5.08 5.93 1.13 21.42-5.93 35.55-4.79 9.58-10.6 16.21-23.16 25.19-14.15 10.1-35.5 12.2-40.71 3.85-1.86-2.97-2.1-8.14-1.06-15.73.78-5.68 1.86-10.71 4.73-22.98l.12-.51c1.59-6.8 2.37-10.31 3.14-14.14 1.45-7.25 3.74-11.47 7.26-13.74 2.81-1.8 5.53-2.28 12.33-2.62 5.33-.27 7.56-.46 10.81-1.03zm.18.98c-3.3.59-5.56.78-10.94 1.05-6.62.33-9.23.78-11.84 2.46-3.25 2.1-5.42 6.09-6.82 13.1-.77 3.84-1.56 7.35-3.15 14.17l-.12.5c-2.86 12.24-3.93 17.26-4.7 22.9-1.03 7.36-.79 12.36.9 15.07 4.82 7.7 25.54 5.67 39.29-4.15 12.43-8.88 18.13-15.39 22.84-24.81 6.86-13.72 10.75-29 6.07-34.45-5.84-6.81-15.7-8.65-31.53-5.84zM132 276.5c7.12 0 10.66 3.08 11.25 8.7.42 4.02-.43 8.14-2.77 15.94-2.56 8.52-18.36 25.38-27.2 31.28-7.01 4.67-20.02 5.67-26.57.99-3.99-2.85-3.53-12.08.02-26.46.68-2.75 1.47-5.65 2.37-8.76a412.6 412.6 0 0 1 3.05-10.14l.37-1.2c1.48-4.8 5.1-7.75 10.73-9.27 4.4-1.2 9.54-1.5 17.48-1.33l3.89.1c3.87.11 5.42.15 7.38.15zm0 1c-1.97 0-3.53-.04-7.41-.15l-3.88-.1c-7.85-.17-12.92.13-17.2 1.3-5.32 1.43-8.67 4.16-10.03 8.6a1277.83 1277.83 0 0 1-1.6 5.21c-.68 2.2-1.27 4.17-1.82 6.1-.9 3.1-1.68 5.99-2.36 8.73-3.43 13.88-3.87 22.93-.4 25.4 6.17 4.42 18.73 3.45 25.42-1 8.66-5.78 24.33-22.49 26.8-30.73 2.3-7.67 3.14-11.71 2.73-15.56-.53-5.1-3.64-7.8-10.25-7.8zm-17.79 7a31.3 31.3 0 0 1 8.57 1.4c5.42 1.78 8.72 5.03 8.72 10.1 0 9.59-9.51 17.2-22.34 21.47-9.82 3.28-13.62-1.79-11.66-16.54.84-6.28 3.82-10.67 8.24-13.46a20.38 20.38 0 0 1 8.47-2.97zm-.6 1.08a19.39 19.39 0 0 0-7.34 2.73c-4.18 2.64-6.98 6.78-7.77 12.76-1.89 14.11 1.36 18.45 10.34 15.46C121.3 312.37 130.5 305 130.5 296c0-4.56-2.98-7.5-8.03-9.15a28.05 28.05 0 0 0-8.2-1.35c-.13 0-.35.03-.66.08zm80.87-23.45c-2.72 9.8-14.93 9.86-26.72 3.3-10.17-5.64-13.8-17.98-5-22.87a66.53 66.53 0 0 0 4.48-2.7l2.03-1.3a50.15 50.15 0 0 1 3.92-2.3c4.73-2.43 8.82-2.8 14-.72 9.16 3.66 10.98 13.33 7.3 26.6zm-20.83-24.98a49.26 49.26 0 0 0-3.84 2.25l-2.03 1.3c-.84.53-1.5.95-2.16 1.35-.82.5-1.6.96-2.38 1.39-7.94 4.4-4.59 15.8 5 21.12 11.31 6.29 22.8 6.23 25.28-2.7 3.57-12.83 1.85-21.97-6.7-25.4-4.9-1.95-8.69-1.62-13.17.7zm17.85 12.15c0 5.7-2.44 9-6.64 9.96-3.3.76-7.56-.05-11.08-1.81l-1.89-.94c-.67-.34-1.18-.62-1.63-.88-4.07-2.38-4.13-4.97.34-10.93 6.8-9.06 20.9-7.16 20.9 4.6zm-1 0c0-5.3-2.87-8.55-7.32-9.16-4.23-.57-8.99 1.44-11.78 5.16-4.15 5.54-4.1 7.44-.64 9.47.44.25.93.51 1.59.85l1.87.93c3.34 1.67 7.36 2.44 10.42 1.74 3.73-.86 5.86-3.74 5.86-9zm196.5 281c0-12.8 2.44-16.74 18.48-29.77a56.8 56.8 0 0 1 7.61-5.2c2.6-1.5 5.33-2.82 8.5-4.18 1.24-.53 2.48-1.05 4.1-1.7l3.92-1.57c9.4-3.83 13.74-6.7 16.62-12.05 1.2-2.22 2.21-4.4 3.23-6.83a148.57 148.57 0 0 0 1.54-3.84l.3-.74.56-1.44c3.2-8.02 6.05-12.08 12.7-16.5a35.26 35.26 0 0 0 4.96-4 46.36 46.36 0 0 0 3.88-4.29c.27-.34 2.55-3.2 3.2-3.98 3.48-4.15 6.51-5.9 11.51-5.9 3.08 0 5.62-.63 9.57-2.1 5.42-2.02 6.53-2.34 8.96-2.2 2.53.13 4.85 1.26 7.18 3.59 1.3 1.3 5.55 5.83 6.52 6.78 5.06 5 9.44 6.92 17.77 6.92a197.5 197.5 0 0 1 12.08.45c15.93.87 21.94.57 25.28-2.21 6.91-5.77 11.64-2.73 11.64 7.76 0 10.73-8.6 20-19 20-4.8 0-8.32 1.43-9.34 3.67-1.12 2.48.68 6.15 5.98 10.57 13.6 11.33 11.24 20.76-7.64 20.76a21.91 21.91 0 0 0-14.6 5.24c-3.28 2.71-5.8 5.86-9.85 11.82l-1.52 2.25c-3.1 4.57-5.01 7.1-7.32 9.4-6.21 6.21-9.3 7.64-13.05 6.89l-1-.23a10.82 10.82 0 0 0-2.66-.37c-1.6 0-2.41.67-8.18 6.22-4.85 4.67-8.07 6.78-11.82 6.78-1.33 0-3.46 1.15-6.45 3.45-1.27.98-2.68 2.14-4.5 3.7l-4.92 4.29a181.11 181.11 0 0 1-4.54 3.82c-9.33 7.56-15.63 10.2-20.21 6.52-2.7-2.15-4.14-4.51-4.63-7.26-.37-2.04-.26-3.63.29-7.3.87-5.85.65-8.42-1.83-11.6-2.32-2.98-2.96-3.22-3.77-2.39-.25.26-1.35 1.63-1.61 1.94-2.21 2.5-4.85 3.57-9 2.82-4.6-.84-5.57-4.11-4.72-10.09l.24-1.56c.6-3.66.68-4.93.25-5.8-.44-.86-1.9-.94-5.23.4l-.74.29c-13.78 5.54-15.26 6.09-19.43 6.67-6.03.84-9.31-1.6-9.31-7.9zm2 0c0 5 2.14 6.6 7.04 5.92 3.91-.55 5.43-1.1 18.95-6.55l.75-.3c4.17-1.66 6.7-1.54 7.76.58.71 1.43.62 2.76-.06 7l-.24 1.53c-.72 5.04-.06 7.27 3.09 7.84 3.43.62 5.38-.17 7.15-2.18.2-.23 1.34-1.66 1.68-2 1.9-1.96 3.82-1.25 6.78 2.55 2.9 3.74 3.17 6.77 2.22 13.12-1 6.75-.52 9.4 3.62 12.71 3.49 2.8 9.1.45 17.7-6.51 1.35-1.1 2.75-2.28 4.49-3.78l4.93-4.3c1.84-1.58 3.27-2.76 4.58-3.77 3.34-2.56 5.74-3.86 7.67-3.86 3.04 0 5.95-1.9 10.43-6.22l2.46-2.39c.94-.89 1.67-1.56 2.37-2.13 1.81-1.49 3.3-2.26 4.74-2.26 1.03 0 1.81.13 3.1.42.7.16.71.17.96.21 2.96.6 5.45-.55 11.23-6.33 2.2-2.2 4.06-4.65 7.09-9.11l1.52-2.25c4.15-6.11 6.76-9.37 10.22-12.24a23.9 23.9 0 0 1 15.88-5.7c16.87 0 18.62-7.01 6.36-17.23-5.9-4.92-8.12-9.41-6.52-12.93 1.42-3.12 5.67-4.84 11.16-4.84 9.25 0 17-8.34 17-18 0-8.94-2.88-10.79-8.36-6.23-3.94 3.28-9.98 3.59-26.67 2.68l-1.02-.06c-5.09-.27-7.99-.39-10.95-.39-8.88 0-13.76-2.14-19.18-7.5-1-.98-5.26-5.53-6.53-6.79-1.99-1.99-3.86-2.9-5.87-3-2.03-.12-3.06.18-8.15 2.07-4.15 1.55-6.9 2.22-10.27 2.22-4.33 0-6.84 1.46-9.98 5.2-.63.74-2.89 3.6-3.18 3.95a48.29 48.29 0 0 1-4.04 4.46 37.26 37.26 0 0 1-5.24 4.23c-6.26 4.17-8.9 7.91-11.95 15.58l-.57 1.43-.28.74a531.5 531.5 0 0 1-1.56 3.88 77.49 77.49 0 0 1-3.32 7c-3.16 5.88-7.82 8.97-17.63 12.96l-3.92 1.58c-1.6.64-2.84 1.15-4.05 1.67a79.2 79.2 0 0 0-8.3 4.08 54.8 54.8 0 0 0-7.35 5.02c-15.62 12.7-17.74 16.13-17.74 28.23zm133.22-79.76c3.06 1.53 6.54 2.02 10.68 1.7 2.53-.2 4.91-.62 8.8-1.49 5.36-1.19 6.33-1.38 8.33-1.54 2.78-.23 4.82.17 6.29 1.4 1.58 1.31 1.96 2.72 1.26 4.22-.66 1.38-1.05 1.74-5.05 5.07-3.53 2.93-5.03 4.83-5.03 7.09 0 7.3 1.29 10.02 7.83 15.62 3.86 3.3 5.93 6.84 5.28 9.62-.75 3.25-4.96 5.02-12.61 5.02-7.18 0-12.7 4.61-20.03 14.68-.5.7-3.96 5.57-4.94 6.87a38.89 38.89 0 0 1-4.72 5.5c-1.06.98-2.09 1.7-3.1 2.15-2.85 1.26-5.05 1.57-9.83 1.74-7.66.27-10.87 1.45-14.98 7.1-1.58 2.17-3.11 4-4.68 5.6a42.87 42.87 0 0 1-8.65 6.69c-.15.08-10.69 6.19-14.8 8.83-3.76 2.42-6.45 2.04-8.22-.77-1.28-2.03-1.9-4.54-2.87-10.35-.84-5.08-1.27-7.08-2.06-8.93-.97-2.3-2.21-3.24-4.02-2.88-6.2 1.24-8.95 1.39-10.98.2-2.37-1.4-3.13-4.62-2.62-10.73.16-1.96-1.04-2.87-3.76-3.04-2.24-.13-4.9.2-9.94 1.12l-.69.12c-7.97 1.45-10.72 1.72-12.72.73-2.91-1.43-1.6-5.27 4.23-12.21 5.48-6.53 10.6-10.81 15.76-13.53 3.74-1.97 5.94-2.65 12.16-4.1 7.29-1.72 10.4-3.51 14.04-9.31 2.96-4.75 10.74-18.62 12.14-20.84 3.59-5.67 6.8-9.1 11.05-11.34 2.6-1.38 4.72-2.82 9.17-6.07l1.38-1.01c7.85-5.72 12.3-7.98 17.68-7.98 4.22 0 6.49 1.36 9.13 4.77.34.43 1.67 2.22 2 2.67.85 1.09 1.6 1.98 2.45 2.83a24.29 24.29 0 0 0 6.64 4.78zm-.44.9c-2.8-1.4-5-3.03-6.92-4.97-.87-.9-1.65-1.81-2.51-2.93-.35-.46-1.68-2.25-2.01-2.67-2.47-3.18-4.46-4.38-8.34-4.38-5.09 0-9.4 2.2-17.09 7.78l-1.38 1.01c-4.49 3.29-6.63 4.74-9.3 6.15-4.06 2.15-7.16 5.45-10.66 11-1.39 2.19-9.16 16.05-12.15 20.82-3.79 6.07-7.13 7.98-14.66 9.75-6.13 1.45-8.27 2.1-11.92 4.02-5.04 2.66-10.05 6.86-15.46 13.3-5.43 6.46-6.53 9.69-4.55 10.66 1.7.84 4.48.57 12.1-.81l.7-.13c5.12-.93 7.82-1.27 10.17-1.12 3.21.2 4.92 1.48 4.7 4.11-.48 5.76.2 8.64 2.13 9.78 1.73 1.02 4.34.88 10.27-.31 2.35-.47 4 .78 5.14 3.47.83 1.95 1.27 4 2.07 8.8l.06.36c.94 5.65 1.55 8.11 2.72 9.98 1.46 2.3 3.52 2.6 6.84.46 4.14-2.66 14.69-8.77 14.81-8.85a41.9 41.9 0 0 0 8.46-6.54 47.89 47.89 0 0 0 4.6-5.48c4.32-5.95 7.81-7.23 15.74-7.5 4.66-.17 6.76-.47 9.46-1.67.9-.4 1.85-1.06 2.84-1.96a38.03 38.03 0 0 0 4.6-5.36c.96-1.3 4.4-6.16 4.93-6.87 7.5-10.31 13.22-15.09 20.83-15.09 7.24 0 11.02-1.6 11.64-4.24.54-2.32-1.36-5.55-4.97-8.64-6.75-5.79-8.17-8.79-8.17-16.38 0-2.67 1.64-4.74 5.39-7.86 3.8-3.17 4.23-3.56 4.78-4.73.5-1.06.25-1.99-.99-3.03-2.23-1.85-4.72-1.65-13.76.36-3.93.87-6.35 1.3-8.94 1.5-4.3.34-7.97-.18-11.2-1.8zm-28-3.9c5.65-2.82 8.96-2.2 12.9 1.37.56.5 2.6 2.47 3.02 2.87 4.2 3.89 8.07 5.71 14.3 5.71 11.37 0 14 1.41 16.1 8.09.26.83 1.35 4.6 1.66 5.62.8 2.63 1.64 5.03 2.7 7.6 2.13 5.17 2.64 8.32 1.72 10.24-.77 1.61-2.1 2.18-5.37 2.79-2.32.43-2.8.53-3.85.85-1.85.58-3.35 1.4-4.6 2.66-1 1-2.02 2.13-3.31 3.66-.6.71-2.91 3.5-3.46 4.14-7.2 8.54-12.43 12.35-19.59 12.35-3.76 0-6.95 1.28-10.59 4-1.84 1.37-11.62 10.31-15.22 13.06a73.09 73.09 0 0 1-8.95 5.88c-4.58 2.54-7.35 3.22-8.98 2.23-1.32-.8-1.65-2.07-1.94-5.5a52.53 52.53 0 0 0-.16-1.81c-.54-4.73-2.24-6.86-7.16-6.86-7.11 0-8.85-1.23-9.73-5.41-.96-4.61-2.1-6.7-6.55-9.67-3.97-2.65-4.31-5.42-1.52-8.22 2-2 4.63-3.5 11.35-6.87 6.61-3.3 9.2-4.8 11.1-6.68a39.09 39.09 0 0 0 5.3-6.48c.98-1.5 1.83-3.04 2.88-5.13l2.12-4.3c.91-1.83 1.72-3.37 2.61-4.98 5.74-10.32 10.37-14.78 23.22-21.2zm-22.34 21.7c-.89 1.59-1.69 3.12-2.6 4.94l-2.11 4.3a52.9 52.9 0 0 1-2.94 5.23 40.08 40.08 0 0 1-5.44 6.63c-2 2-4.62 3.51-11.35 6.87-6.6 3.3-9.2 4.8-11.1 6.69-2.33 2.34-2.08 4.37 1.38 6.67 4.7 3.14 5.96 5.46 6.97 10.3.78 3.7 2.09 4.62 8.75 4.62 5.5 0 7.57 2.57 8.15 7.75.06.5.09.82.17 1.84.25 3.06.55 4.17 1.46 4.72 1.2.74 3.69.13 7.98-2.25a72.09 72.09 0 0 0 8.82-5.8c3.55-2.7 13.34-11.65 15.24-13.07 3.79-2.83 7.18-4.19 11.18-4.19 6.77 0 11.8-3.67 18.83-12l3.45-4.13a60.07 60.07 0 0 1 3.37-3.72 11.72 11.72 0 0 1 5.01-2.91c1.1-.34 1.6-.45 3.97-.89 2.95-.55 4.07-1.02 4.65-2.23.76-1.59.28-4.5-1.74-9.43a84.46 84.46 0 0 1-2.74-7.69c-.31-1.03-1.4-4.8-1.66-5.61-1.95-6.2-4.16-7.39-15.14-7.39-6.5 0-10.61-1.93-14.98-5.98-.44-.4-2.46-2.37-3.01-2.86-3.65-3.3-6.52-3.85-11.79-1.21-12.67 6.33-17.15 10.65-22.78 20.8zm55.86 11.93c-2.98 6.45-16.78 15.26-26.74 15.26-5.33 0-7.56-2.98-7.11-7.86.32-3.48 2.1-7.91 3.93-10.61l1.52-2.32a44.95 44.95 0 0 1 1.88-2.7c3.66-4.8 7.85-7.45 13.62-7.45 9.06 0 15.75 9.52 12.9 15.68zm-.9-.42c2.52-5.47-3.65-14.26-12-14.26-5.4 0-9.33 2.48-12.82 7.06-.6.8-1.17 1.6-1.85 2.64 0 0-1.2 1.87-1.52 2.33-1.74 2.57-3.46 6.85-3.77 10.14-.4 4.33 1.43 6.77 6.12 6.77 9.57 0 23.02-8.58 25.83-14.68zm-69.67 20.74c2.08.18 4.44.81 5.88 1.8 2.12 1.47 2.2 3.6-.26 6.05-5.14 5.15-12.85 4.34-12.85-1.35 0-4.66 3.14-6.84 7.23-6.5zm-.09 1c-3.56-.3-6.14 1.5-6.14 5.5 0 4.58 6.53 5.26 11.15.65 2.03-2.04 1.98-3.43.4-4.52-1.27-.88-3.48-1.47-5.4-1.63zm29.59-225.95c4.64 2.35 17.27 8.24 19.39 9.43a24.14 24.14 0 0 1 7.05 5.64 45.03 45.03 0 0 1 3.75 5.2c2.4 3.78.04 7.66-6.2 11.63-4.97 3.16-12.18 6.3-21.95 9.82-4.84 1.74-19.63 6.68-21.1 7.2-6.59 2.33-14.85.1-25.14-5.86-3.93-2.27-8-5-12.94-8.54-2.23-1.61-9.5-6.99-10.7-7.85a81.21 81.21 0 0 0-8.63-5.7c-4.82-2.6-4.45-6.64.17-12.13 3.27-3.88 4.17-4.67 18.1-16.33a230.2 230.2 0 0 0 8.89-7.74 95.2 95.2 0 0 0 4.72-4.66c5.08-5.43 9.8-6.49 14.97-3.92 2.24 1.1 4.53 2.85 7.43 5.52 1.48 1.37 6.94 6.72 7.98 7.7 5.2 4.91 9.46 8.2 14.2 10.6zm-.46.9c-4.85-2.45-9.18-5.79-14.44-10.76-1.05-1-6.5-6.34-7.97-7.69-2.83-2.61-5.06-4.3-7.2-5.37-4.75-2.36-9-1.4-13.8 3.71a96.18 96.18 0 0 1-4.76 4.71c-2.48 2.3-5.16 4.62-8.92 7.77-13.86 11.6-14.77 12.4-17.98 16.21-4.28 5.08-4.58 8.4-.46 10.61 2.23 1.2 4.9 2.99 8.74 5.77 1.2.87 8.47 6.24 10.7 7.85a154.8 154.8 0 0 0 12.85 8.49c10.06 5.82 18.07 7.98 24.3 5.78 1.48-.52 16.27-5.47 21.1-7.2 9.7-3.5 16.86-6.61 21.75-9.72 5.84-3.71 7.9-7.1 5.9-10.26a44.09 44.09 0 0 0-3.67-5.08 23.16 23.16 0 0 0-6.78-5.42c-2.08-1.16-14.68-7.05-19.36-9.4zm-38.83 8.05c3.11-.37 5.7-.13 8.4.7 2.15.66 2.74.93 8.64 3.77 4.75 2.29 8.39 3.86 13.19 5.56 8.38 2.97 11.32 6.23 8.83 9.76-2.08 2.94-8.04 5.92-17.84 9.18-8.45 2.82-15.48 2.35-21.43-.9-4.65-2.55-8.33-6.5-12.15-12.3-2.9-4.41-2.73-8.2.16-11.06 2.48-2.45 6.87-4.07 12.2-4.7zm.12 1c-5.13.6-9.33 2.16-11.62 4.42-2.53 2.5-2.68 5.77-.02 9.8 3.73 5.68 7.3 9.51 11.8 11.97 5.7 3.11 12.43 3.57 20.62.84 9.59-3.2 15.44-6.12 17.34-8.82 1.94-2.75-.5-5.45-8.35-8.24-4.84-1.72-8.5-3.3-13.28-5.6-5.84-2.81-6.42-3.07-8.5-3.71a18.42 18.42 0 0 0-8-.66zM202.5 500.38c0 4.78-1.45 7.56-4.43 8.93-2.29 1.05-4.55 1.23-10.79 1.2l-1.78-.01c-9.19 0-17-7.65-17-15.5 0-7.59 10.6-10.51 19.74-5.44 2.78 1.55 4.21 1.94 8.57 2.75 4.44.83 5.69 2.27 5.69 8.07zm-1 0c0-5.3-.9-6.34-4.88-7.08-4.45-.83-5.96-1.25-8.86-2.86-8.57-4.76-18.26-2.1-18.26 4.56 0 7.3 7.36 14.5 16 14.5h1.79c6.06.04 8.26-.14 10.36-1.1 2.6-1.2 3.85-3.6 3.85-8.02zm33.33-117.85c3.71-1.31 8.7-2.7 16.1-4.55 2.58-.65 16.53-4.04 20.56-5.05 19.59-4.93 31.55-8.9 38.23-13.35 14.93-9.95 36.87-33.88 43.83-47.8 2.25-4.5 4.65-6.38 7.68-6.25 1.26.06 2.61.45 4.32 1.2a50.81 50.81 0 0 1 3.54 1.7l1.26.63c4.78 2.34 8.38 3.44 12.65 3.44 7.2 0 10.01 3.07 8.35 7.91-1.4 4.06-5.92 8.91-11.1 12.02-8.3 4.98-11.75 17.3-11.75 33.57 0 3.59-1.37 6.28-3.98 8.36-1.98 1.58-4.2 2.6-8.47 4.16l-1.02.37c-4.85 1.75-6.98 2.77-8.68 4.46-5.09 5.1-12.54 7.15-20.35 7.15-1.38 0-2.47.92-3.99 3.1-.29.41-1.32 1.95-1.47 2.18-2.68 3.92-4.93 5.72-8.54 5.72-7.84 0-10.74.93-21.76 6.94-5.18 2.82-8.8 3.58-14.66 3.68-.26 0-.47 0-.92.02-4.82.06-7.12.3-10.51 1.34a73.43 73.43 0 0 0-8.89 3.56c-2.17 1-10.53 5.01-10.23 4.87-7.79 3.7-13.32 5.98-18.9 7.57-12.41 3.55-18.58 2.24-27.42-4.07-2.58-1.85-2.72-4.43-.83-7.62 1.45-2.45 3.9-5.09 8.08-8.97l1.78-1.64c3.92-3.6 4.48-4.11 5.9-5.53 2.32-2.32 3.12-3.5 5.48-7.63 1.93-3.36 3.37-5.11 6.27-7.06 2.3-1.54 5.34-2.98 9.44-4.43zm.34.94c-4.03 1.42-7 2.83-9.22 4.32-2.75 1.85-4.1 3.49-5.96 6.73-2.4 4.2-3.24 5.44-5.64 7.83-1.43 1.44-2 1.96-5.94 5.57l-1.77 1.63c-4.1 3.82-6.52 6.41-7.9 8.75-1.65 2.79-1.54 4.8.55 6.3 8.6 6.14 14.46 7.38 26.57 3.92 5.5-1.57 11-3.84 18.74-7.51-.3.14 8.06-3.88 10.24-4.88a74.3 74.3 0 0 1 9.01-3.6c3.51-1.09 5.89-1.33 10.8-1.4h.91c5.72-.1 9.18-.83 14.2-3.57 11.16-6.08 14.2-7.06 22.24-7.06 3.19 0 5.2-1.6 7.71-5.28l1.48-2.2c1.7-2.43 3-3.52 4.81-3.52 7.57 0 14.78-2 19.65-6.85 1.83-1.84 4.04-2.9 9.04-4.7l1.02-.37c8.6-3.13 11.79-5.67 11.79-11.58 0-16.6 3.53-29.2 12.24-34.43 5-3 9.35-7.67 10.66-11.48 1.42-4.13-.83-6.59-7.4-6.59-4.45 0-8.19-1.14-13.09-3.54-7.52-3.67-6.78-3.34-8.72-3.43-2.58-.1-4.65 1.52-6.74 5.7-7.04 14.07-29.1 38.14-44.17 48.19-6.81 4.54-18.84 8.52-38.55 13.48-4.03 1.02-17.98 4.4-20.56 5.05-7.37 1.84-12.33 3.23-16 4.52zM252 387.5c2.08 0 4-.2 7.25-.69 5.22-.77 6.64-.9 8.46-.5 2.52.56 3.79 2.35 3.79 5.69 0 4.05-2.27 7.29-6.62 10.11-3.24 2.1-6.53 3.53-14.15 6.4l-.27.1-2.28.86c-3.04 1.16-5.27 2.52-9.33 5.43l-.8.57c-8.19 5.88-13.35 8.03-23.05 8.03-4.98 0-6.88-2.03-5.75-5.62.87-2.81 3.58-6.56 7.8-11.13 1.26-1.37 2.64-2.8 4.15-4.3 3.17-3.14 11.25-10.61 11.45-10.8.46-.47.93-.89 1.4-1.26 3.38-2.71 5.77-3.08 14.18-2.93 1.65.03 2.63.04 3.77.04zm0 1c-1.15 0-2.13-.01-3.79-.04-8.18-.14-10.4.2-13.54 2.71-.44.35-.88.74-1.32 1.18-.2.21-8.3 7.69-11.45 10.82a134.6 134.6 0 0 0-4.12 4.26c-4.12 4.47-6.76 8.12-7.58 10.75-.9 2.88.45 4.32 4.8 4.32 9.46 0 14.44-2.07 22.46-7.84l.8-.57c4.13-2.96 6.42-4.36 9.56-5.56l2.3-.86.25-.1c7.55-2.84 10.8-4.25 13.97-6.3 4.08-2.65 6.16-5.6 6.16-9.27 0-2.89-.97-4.26-3-4.7-1.65-.37-3.05-.25-8.1.5-3.3.5-5.26.7-7.4.7zm112.47-45.34c-1.88 5.44-1.98 6.76-.98 12.76 1.18 7.06-1.38 16.58-5.49 16.58a16.89 16.89 0 0 0-1.51.07l-.64.04c-2.86.18-4.83.17-6.94-.17-6.55-1.06-10.41-5.14-10.41-13.44 0-13.9 2.14-19.69 8.13-26.33a21.9 21.9 0 0 0 2.52-3.75c.59-1.03 2.78-5.13 2.72-5.01 4.44-8.14 7.71-11.53 12.25-10.4 1.17.3 2.2.77 3.58 1.59l1.39.84a20 20 0 0 0 3.1 1.6c.7.27 1.8.32 4.75.26l.72-.01c3.16-.05 4.78.08 5.83.66 1.61.89 1.2 2.56-1.14 4.9a215.9 215.9 0 0 1-3.86 3.76c-10.6 10.1-12.75 12.4-14.02 16.05zm-.94-.32c1.34-3.9 3.46-6.17 14.27-16.46 1.55-1.47 2.73-2.62 3.85-3.73 1.94-1.95 2.17-2.88 1.35-3.33-.82-.45-2.37-.58-5.32-.53l-.72.01c-3.14.06-4.26.02-5.14-.34-1.06-.41-1.97-.9-3.25-1.67l-1.38-.83a12.1 12.1 0 0 0-3.31-1.47c-3.88-.97-6.92 2.17-11.13 9.9.07-.13-2.14 3.98-2.73 5.02a22.71 22.71 0 0 1-2.65 3.92c-5.81 6.47-7.87 12-7.87 25.67 0 7.79 3.48 11.47 9.57 12.45 2.01.33 3.92.34 6.71.16a371.33 371.33 0 0 0 1.23-.07c.42-.03.73-.04.99-.04 3.2 0 5.6-8.9 4.5-15.42-1.02-6.16-.91-7.64 1.03-13.24zm-9.26 12.42c.58.52 2.5 1.9 2.55 1.93 1.96 1.57 2.04 3.31.01 6.36-3.74 5.64-8.83 3.09-8.83-4.55 0-3.81.51-5.67 2.07-6.02 1.18-.26 2 .3 4.2 2.28zm-1.34 1.48c-1.5-1.35-2.23-1.85-2.43-1.8-.17.03-.5 1.23-.5 4.06 0 5.87 2.67 7.21 5.17 3.45 1.5-2.26 1.47-2.84.4-3.7.03.03-1.95-1.4-2.64-2zm222.9-130.19c2.2-1.1 3.67-1.66 5.88-2.36l.28-.09a48.92 48.92 0 0 0 8.79-3.55c4.17-2.08 6.35-1.88 6.96.84.44 2 .2 4.01-1.25 12.7-2.27 13.62-9.16 26.14-21.17 36.3-4.3 3.63-7.41 4.39-9.75 2.44-1.88-1.57-3.1-4.57-4.61-10.48-.3-1.15-1.43-5.83-1.72-6.96a114.18 114.18 0 0 0-2.71-9.22c-2.4-6.82-3.03-10.78-2.1-12.94.77-1.83 2.08-2.24 5.6-2.45 1.49-.09 2.09-.14 2.97-.28l1.95-.33c.72-.12 1.22-.2 1.68-.29 1.1-.2 1.92-.38 2.71-.6 1.7-.49 3.42-1.2 6.49-2.73zm.44.9c-3.11 1.54-4.88 2.29-6.65 2.79-.84.23-1.69.42-2.81.63a108.77 108.77 0 0 1-3.81.63c-.77.13-1.39.19-2.92.28-3.13.18-4.17.51-4.74 1.85-.78 1.84-.2 5.62 2.13 12.2a115.12 115.12 0 0 1 2.74 9.31l1.72 6.96c1.46 5.7 2.62 8.58 4.28 9.96 1.87 1.56 4.49.93 8.47-2.44 11.82-10 18.6-22.3 20.83-35.7 1.4-8.45 1.65-10.51 1.25-12.31-.41-1.87-1.86-2-5.54-.16a49.87 49.87 0 0 1-8.93 3.6l-.28.1a35.4 35.4 0 0 0-5.74 2.3zm-4.5 6.58c1.37-.32 2.5-.75 3.9-1.42.35-.18 2.57-1.31 3.32-1.67 1.5-.71 2.97-1.31 4.7-1.89 2.7-.9 4.64-.77 5.88.4.98.94 1.34 2.26 1.41 4.18.02.4.02.7.02 1.37 0 5.63-4.63 16.88-11.34 22.75-4.34 3.8-7.31 4.67-9.92 2.52-2.06-1.7-3.5-4.65-6.67-12.91-1.86-4.83-2.05-8.1-.68-10.2 1.12-1.7 2.9-2.36 5.83-2.7l1.26-.12c1.19-.12 1.75-.19 2.3-.31zm-2.1 2.3-1.22.12c-2.4.27-3.7.76-4.39 1.81-.93 1.43-.78 4.1.87 8.38 3.02 7.84 4.41 10.71 6.08 12.09 1.63 1.34 3.64.75 7.33-2.48C584.6 250.77 589 240.08 589 235c0-.64 0-.93-.02-1.29-.05-1.44-.3-2.33-.79-2.8-.6-.57-1.8-.65-3.87.04a37.95 37.95 0 0 0-4.47 1.8c-.72.34-2.93 1.47-3.32 1.66a19.54 19.54 0 0 1-4.3 1.56c-.66.16-1.28.24-2.56.36zm-227.73-88.98c-1.59 4.3-3.54 7.25-7.14 11.4l-2.6 2.97a67.02 67.02 0 0 0-2.63 3.23 46.4 46.4 0 0 0-4.68 7.5c-2.85 5.7-7.14 10.18-12.85 13.89-4.25 2.76-8.25 4.62-15.67 7.59-11.01 4.4-16.43 1.26-27.22-16.4-2.86-4.69-8.8-8.63-17.98-12.66-3-1.33-12.88-5.24-14.43-5.92-4.96-2.18-7.04-3.72-6.42-5.85.67-2.32 5.3-4.05 15.48-6.08 16.63-3.32 26.93-3.82 39.93-3.02 7.9.49 9.67.5 12.74-.26 1.99-.48 3.92-1.3 6-2.6l2.79-1.71c9.86-6.14 12.94-7.96 17.3-9.9 6.03-2.71 10.57-3.32 13.94-1.4 7.2 4.12 7.68 7.7 3.44 19.22zm-1.88-.7c3.95-10.7 3.6-13.26-2.56-16.78-2.66-1.52-6.62-.99-12.12 1.48-4.24 1.9-7.3 3.7-17.07 9.77l-2.79 1.73a22.6 22.6 0 0 1-6.57 2.84c-3.36.81-5.22.8-13.34.3-12.84-.78-22.97-.29-39.41 3-4.9.97-8.45 1.88-10.79 2.75-2.03.76-3.04 1.45-3.17 1.91-.16.57 1.48 1.79 5.3 3.46 1.5.67 11.39 4.58 14.44 5.93 9.52 4.19 15.74 8.3 18.87 13.44 10.35 16.93 14.87 19.56 24.78 15.6 7.3-2.93 11.21-4.75 15.33-7.42 5.42-3.53 9.47-7.75 12.15-13.1 1.44-2.9 3.02-5.4 4.86-7.82a68.95 68.95 0 0 1 2.72-3.33l2.6-2.97c3.46-3.99 5.28-6.75 6.77-10.79zm-6.64-.39c-7.94 12.8-18.53 21.75-33.3 25.23-7.82 1.83-12.47-.79-13.12-5.93-.55-4.45 2.29-9.06 6-9.06 3.02 0 5.6-1.68 15.38-9.16 1.47-1.12 2.57-1.96 3.66-2.74 4.4-3.2 7.77-5.17 10.82-6.08 5.57-1.67 9.33-2.15 11.35-1.22 2.5 1.14 2.22 4.13-.79 8.96zm-.84-.52c2.72-4.4 2.94-6.74 1.21-7.53-1.71-.79-5.32-.33-10.65 1.27-2.9.87-6.2 2.79-10.51 5.92-1.08.79-2.18 1.62-3.65 2.74-10.08 7.72-12.62 9.36-15.98 9.36-3.02 0-5.5 4.02-5 7.94.56 4.5 4.62 6.78 11.89 5.07 14.48-3.4 24.86-12.18 32.69-24.77zM461.17 33.53c13.88 4.96 20.75 4.96 31.62.01 3.02-1.37 5.47-2.94 11-6.82 5.57-3.92 8.05-5.51 11.14-6.92 4.14-1.88 7.78-2.38 11.22-1.28 3.92 1.26 6.2 12.3 6.78 28.45.5 14.2-.52 28.93-2.46 34.2-1.82 4.93-5.86 8.17-11.51 10.02A41.7 41.7 0 0 1 506 93.01c-5.79 0-9 2.4-12.2 7.64-.37.59-1.55 2.6-1.71 2.87-1.75 2.9-3.05 4.33-4.93 4.95-.94.32-2.07.83-3.87 1.74l-2.43 1.23c-1.03.53-1.87.94-2.7 1.34-6.43 3.1-11.73 4.72-17.16 4.72-5.71 0-10.04 2.09-14.02 5.92-1.16 1.11-4.2 4.53-4.63 4.94-2.54 2.44-5.93 4.24-10.85 6.1-1.4.52-5.98 2.13-6.25 2.22l-2.06.78c-.89.36-1.78.63-2.7.81-5.55 1.14-11.14-.54-17.98-4.42-1.27-.73-5.13-3.06-5.76-3.42-2.05-1.16-4.12-1.53-9.09-1.9l-1.73-.15c-4.78-.4-7.68-1.14-10.22-2.97-5-3.61-6.77-7.76-5.65-12.33 1.33-5.42 6.5-11.02 14.85-17.28a169.2 169.2 0 0 1 6.5-4.61c-.33.23 4.33-2.92 5.3-3.6 2.73-1.91 4.8-3.9 12.75-12.04l1.09-1.1c3.49-3.56 5.89-5.89 8.12-7.83 2.9-2.5 4.72-5.95 7.5-13.05l.63-1.61c2.7-6.92 4.28-10 6.87-12.33 1.42-1.28 6.68-6.54 7.93-7.5 3.98-3 8.01-2.73 19.57 1.4zm-.34.94c-11.26-4.02-15-4.28-18.62-1.53-1.19.9-6.4 6.11-7.88 7.43-2.42 2.18-3.96 5.19-6.6 11.95l-.63 1.61c-2.83 7.26-4.72 10.8-7.77 13.45a141.85 141.85 0 0 0-9.16 8.87c-8.02 8.2-10.08 10.2-12.88 12.16-.99.69-5.65 3.84-5.31 3.6-2.5 1.71-4.52 3.13-6.47 4.59-8.17 6.13-13.23 11.6-14.48 16.72-1.02 4.15.58 7.9 5.26 11.27 2.36 1.7 5.11 2.4 9.72 2.8l1.73.13c5.12.4 7.28.78 9.5 2.05.65.36 4.5 2.7 5.76 3.4 6.66 3.78 12.04 5.4 17.29 4.32.86-.17 1.7-.42 2.52-.75a67 67 0 0 1 2.1-.8c.28-.1 4.86-1.7 6.24-2.22 4.8-1.8 8.08-3.56 10.5-5.88.4-.38 3.44-3.8 4.63-4.94 4.16-4 8.72-6.2 14.72-6.2 5.25 0 10.42-1.59 16.73-4.62.82-.4 1.65-.8 2.68-1.33.12-.06 1.93-.99 2.43-1.23 1.84-.93 3-1.46 4-1.8 1.6-.52 2.76-1.82 4.39-4.52l1.7-2.88c3.39-5.5 6.87-8.11 13.07-8.11 4.45 0 8.73-.49 12.64-1.77 5.4-1.76 9.2-4.8 10.9-9.41 1.87-5.11 2.9-19.75 2.39-33.83-.56-15.53-2.81-26.48-6.08-27.52-3.18-1.02-6.57-.55-10.5 1.23-3.02 1.37-5.47 2.94-11 6.83-5.57 3.92-8.05 5.5-11.14 6.92-11.13 5.05-18.26 5.05-32.38.01zM475 55c5.38 0 7.55-.21 9.72-.96 1.26-.43 9.95-4.8 14.88-6.96 1.9-.82 3.56-2.44 6.6-6.04 2.56-3.04 3.19-3.75 4.4-4.84 3.7-3.35 7.07-3.28 10.22 1.23 6.23 8.9 5.61 15.94.07 27.02a71.26 71.26 0 0 0-2.5 5.48c-.32.8-1 2.7-1.09 2.9-.17.45-.34.81-.54 1.17-.63 1.14-1.56 2.21-4.05 4.7-2.4 2.4-5.16 3.27-11.68 4.33-1.81.3-2.2.36-3 .51-6.02 1.1-9.6 2.69-12.24 6.07-3.57 4.59-7.9 7.48-14.98 10.74-.55.24-1.1.5-1.8.8l-1.78.8a60.08 60.08 0 0 0-7.7 3.9c-2.57 1.6-4.79 2.35-9.42 3.46-8.58 2.06-12.28 3.76-17.37 9.36-5.12 5.64-10.17 7.64-16.63 6.7-5.36-.79-10.63-3.01-23.56-9.48-6.3-3.15-6.43-7.78-1.5-13.56 3.38-3.94 3.52-4.06 19.4-16.44 8.12-6.33 12.97-10.57 16.63-14.88 2.53-2.98 4.2-5.73 4.96-8.3 5.5-18.3 12.5-21.98 22.78-15.56 1.95 1.22 6.61 4.55 7.18 4.9 3.36 2.15 6.52 2.95 13 2.95zm0 2c-6.84 0-10.37-.89-14.08-3.26-.63-.4-5.27-3.71-7.16-4.9-9.05-5.65-14.66-2.7-19.8 14.45-.86 2.87-2.67 5.85-5.35 9.01-3.78 4.45-8.7 8.75-16.94 15.17-15.66 12.21-15.86 12.38-19.1 16.16-4.17 4.9-4.09 8 .88 10.48 12.71 6.35 17.89 8.54 22.94 9.28 5.78.84 10.18-.9 14.87-6.06 5.42-5.96 9.45-7.82 18.38-9.96 4.43-1.07 6.5-1.76 8.83-3.22a61.7 61.7 0 0 1 7.94-4.02l1.78-.8 1.78-.8c6.82-3.13 10.91-5.87 14.24-10.14 3-3.87 7-5.64 13.46-6.82.83-.15 1.21-.21 3.04-.51 6.1-1 8.6-1.78 10.58-3.77 2.36-2.36 3.21-3.34 3.72-4.26.15-.27.29-.56.44-.94.06-.15.75-2.06 1.09-2.9.64-1.6 1.45-3.4 2.57-5.64 5.24-10.49 5.8-16.8.07-24.98-2.4-3.44-4.37-3.48-7.24-.89-1.11 1-1.73 1.7-4.22 4.65-3.24 3.85-5.04 5.59-7.32 6.59-4.82 2.1-13.62 6.53-15.03 7.01-2.44.84-4.79 1.07-10.37 1.07zm-12.7 8.6c5.47 3.9 10.34 3.72 18.23.88 5.39-1.94 5.92-2.1 7.7-2.1 2.5-.01 4.21 1.36 5.24 4.46 1.66 4.98-2.32 8.52-12.3 12.68-2.7 1.13-16.25 6.18-20 7.73-7.86 3.24-13.93 6.42-18.87 10.15-13.02 9.84-18.36 11.93-23.71 9.68a24.67 24.67 0 0 1-3.62-1.98l-1.99-1.28a90.4 90.4 0 0 0-2.24-1.4c-3.33-2-2.82-4.28.85-7.34 1.35-1.13 10.66-7.61 13.53-9.91 7.1-5.69 11.91-11.47 14.41-18.34 3.07-8.45 4.89-12.1 6.8-13.39 1.73-1.16 3.36-.53 6.18 1.9.63.56 3.4 3.08 4.11 3.7 1.93 1.7 3.71 3.15 5.67 4.55zm-.6.8c-1.98-1.42-3.79-2.88-5.74-4.6-.73-.64-3.48-3.16-4.1-3.7-2.5-2.16-3.75-2.65-4.97-1.83-1.66 1.11-3.44 4.7-6.42 12.9-2.57 7.07-7.5 12.99-14.72 18.78-2.91 2.33-12.21 8.8-13.52 9.9-3.22 2.68-3.56 4.17-.97 5.72l2.26 1.4 1.99 1.28c1.47.93 2.48 1.5 3.47 1.91 4.9 2.07 9.96.07 22.72-9.56 5.02-3.79 11.15-7 19.1-10.28 3.76-1.55 17.3-6.6 20-7.72 9.5-3.97 13.14-7.2 11.73-11.44-.9-2.71-2.25-3.8-4.3-3.79-1.6 0-2.15.17-7.36 2.05-8.17 2.94-13.34 3.14-19.16-1.01z'/%3E%3C/svg%3E")}.after\:ml-0\.5:after{content:"";margin-left:.125rem}.after\:ml-0:after{content:"";margin-left:0}.after\:text-red-500:after{--tw-text-opacity:1;color:rgba(239,68,68,var(--tw-text-opacity));content:""}.after\:content-\[\'\*\'\]:after{content:"*"}.focus-within\:z-10:focus-within{z-index:10}.focus-within\:text-gray-600:focus-within{--tw-text-opacity:1;color:rgba(75,85,99,var(--tw-text-opacity))}.focus-within\:ring-2:focus-within{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-within\:ring-inset:focus-within{--tw-ring-inset:inset}.focus-within\:ring-indigo-500:focus-within{--tw-ring-opacity:1;--tw-ring-color:rgba(99,102,241,var(--tw-ring-opacity))}.focus-within\:ring-teal-500:focus-within{--tw-ring-opacity:1;--tw-ring-color:rgba(1,157,187,var(--tw-ring-opacity))}.focus-within\:ring-pink-500:focus-within{--tw-ring-opacity:1;--tw-ring-color:rgba(236,72,153,var(--tw-ring-opacity))}.focus-within\:ring-offset-2:focus-within{--tw-ring-offset-width:2px}.focus-within\:ring-offset-gray-100:focus-within{--tw-ring-offset-color:#f3f4f6}.hover\:-translate-y-1:hover{--tw-translate-y:-0.25rem;transform:var(--tw-transform)}.hover\:scale-100:hover{--tw-scale-x:1;--tw-scale-y:1;transform:var(--tw-transform)}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.hover\:border-gray-400:hover{--tw-border-opacity:1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgba(79,70,229,var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgba(55,65,81,var(--tw-bg-opacity))}.hover\:bg-red-500:hover{--tw-bg-opacity:1;background-color:rgba(239,68,68,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgba(5,150,105,var(--tw-bg-opacity))}.hover\:bg-yellow-300:hover{--tw-bg-opacity:1;background-color:rgba(252,211,77,var(--tw-bg-opacity))}.hover\:bg-teal-100:hover,.hover\:bg-teal-700:hover{--tw-bg-opacity:1;background-color:rgba(1,157,187,var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgba(99,102,241,var(--tw-bg-opacity))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.hover\:bg-teal-900:hover{--tw-bg-opacity:1;background-color:rgba(1,157,187,var(--tw-bg-opacity))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgba(156,163,175,var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgba(185,28,28,var(--tw-bg-opacity))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgba(75,85,99,var(--tw-bg-opacity))}.hover\:bg-purple-600:hover{--tw-bg-opacity:1;background-color:rgba(124,58,237,var(--tw-bg-opacity))}.hover\:bg-opacity-50:hover{--tw-bg-opacity:0.5}.hover\:from-indigo-700:hover{--tw-gradient-from:#4338ca;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(67,56,202,0))}.hover\:to-teal-700:hover{--tw-gradient-to:#019dbb}.hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.hover\:text-gray-400:hover{--tw-text-opacity:1;color:rgba(156,163,175,var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgba(17,24,39,var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.hover\:text-indigo-500:hover{--tw-text-opacity:1;color:rgba(99,102,241,var(--tw-text-opacity))}.hover\:text-red-700:hover{--tw-text-opacity:1;color:rgba(185,28,28,var(--tw-text-opacity))}.hover\:text-teal-900:hover{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgba(49,46,129,var(--tw-text-opacity))}.hover\:text-black:hover{--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.hover\:underline:hover{text-decoration:underline}.hover\:drop-shadow-xl:hover{--tw-drop-shadow:drop-shadow(0 20px 13px rgba(0,0,0,0.03)) drop-shadow(0 8px 5px rgba(0,0,0,0.08));filter:var(--tw-filter)}.focus\:z-10:focus{z-index:10}.focus\:border-blue-300:focus{--tw-border-opacity:1;border-color:rgba(147,197,253,var(--tw-border-opacity))}.focus\:border-gray-900:focus{--tw-border-opacity:1;border-color:rgba(17,24,39,var(--tw-border-opacity))}.focus\:border-indigo-300:focus{--tw-border-opacity:1;border-color:rgba(165,180,252,var(--tw-border-opacity))}.focus\:border-red-700:focus{--tw-border-opacity:1;border-color:rgba(185,28,28,var(--tw-border-opacity))}.focus\:border-indigo-700:focus{--tw-border-opacity:1;border-color:rgba(67,56,202,var(--tw-border-opacity))}.focus\:border-gray-300:focus{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.focus\:border-transparent:focus{border-color:transparent}.focus\:border-teal-500:focus{--tw-border-opacity:1;border-color:rgba(1,157,187,var(--tw-border-opacity))}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgba(99,102,241,var(--tw-border-opacity))}.focus\:border-white:focus{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.focus\:border-gray-500:focus{--tw-border-opacity:1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.focus\:bg-indigo-600:focus{--tw-bg-opacity:1;background-color:rgba(79,70,229,var(--tw-bg-opacity))}.focus\:bg-red-600:focus{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.focus\:bg-gray-100:focus{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.focus\:bg-indigo-100:focus{--tw-bg-opacity:1;background-color:rgba(224,231,255,var(--tw-bg-opacity))}.focus\:bg-gray-50:focus{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.focus\:text-gray-700:focus{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.focus\:text-indigo-800:focus{--tw-text-opacity:1;color:rgba(55,48,163,var(--tw-text-opacity))}.focus\:text-gray-800:focus{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.focus\:text-gray-500:focus{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.focus\:text-indigo-600:focus{--tw-text-opacity:1;color:rgba(79,70,229,var(--tw-text-opacity))}.focus\:text-indigo-500:focus{--tw-text-opacity:1;color:rgba(99,102,241,var(--tw-text-opacity))}.focus\:placeholder-gray-400:focus::-moz-placeholder{--tw-placeholder-opacity:1;color:rgba(156,163,175,var(--tw-placeholder-opacity))}.focus\:placeholder-gray-400:focus:-ms-input-placeholder{--tw-placeholder-opacity:1;color:rgba(156,163,175,var(--tw-placeholder-opacity))}.focus\:placeholder-gray-400:focus::placeholder{--tw-placeholder-opacity:1;color:rgba(156,163,175,var(--tw-placeholder-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-2:focus,.focus\:ring:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus,.focus\:ring-1:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-inset:focus{--tw-ring-inset:inset}.focus\:ring-gray-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(209,213,219,var(--tw-ring-opacity))}.focus\:ring-indigo-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(199,210,254,var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(254,202,202,var(--tw-ring-opacity))}.focus\:ring-blue-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(191,219,254,var(--tw-ring-opacity))}.focus\:ring-white:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(255,255,255,var(--tw-ring-opacity))}.focus\:ring-teal-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(1,157,187,var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(99,102,241,var(--tw-ring-opacity))}.focus\:ring-sky-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(14,165,233,var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(79,70,229,var(--tw-ring-opacity))}.focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(239,68,68,var(--tw-ring-opacity))}.focus\:ring-pink-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(236,72,153,var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(107,114,128,var(--tw-ring-opacity))}.focus\:ring-green-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(16,185,129,var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity:0.5}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-gray-100:focus{--tw-ring-offset-color:#f3f4f6}.focus\:ring-offset-gray-50:focus{--tw-ring-offset-color:#f9fafb}.active\:bg-gray-100:active{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.active\:bg-gray-900:active{--tw-bg-opacity:1;background-color:rgba(17,24,39,var(--tw-bg-opacity))}.active\:bg-red-600:active{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.active\:bg-gray-50:active{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.active\:bg-purple-600:active{--tw-bg-opacity:1;background-color:rgba(124,58,237,var(--tw-bg-opacity))}.active\:text-gray-700:active{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.active\:text-gray-500:active{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.active\:text-gray-800:active{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.disabled\:opacity-25:disabled{opacity:.25}.group:hover .group-hover\:bg-gray-200{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.group:hover .group-hover\:text-gray-500{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.group:hover .group-hover\:text-gray-400{--tw-text-opacity:1;color:rgba(156,163,175,var(--tw-text-opacity))}.group:hover .group-hover\:text-gray-700{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.group:hover .group-hover\:text-indigo-600{--tw-text-opacity:1;color:rgba(79,70,229,var(--tw-text-opacity))}.group:hover .group-hover\:text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.group:hover .group-hover\:opacity-75{opacity:.75}@media (min-width:640px){.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-1{grid-column:span 1/span 1}.sm\:col-span-9{grid-column:span 9/span 9}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:mx-0{margin-left:0;margin-right:0}.sm\:mx-auto{margin-left:auto;margin-right:auto}.sm\:-my-px{margin-bottom:-1px;margin-top:-1px}.sm\:mx-2{margin-left:.5rem;margin-right:.5rem}.sm\:ml-3{margin-left:.75rem}.sm\:-mr-2{margin-right:-.5rem}.sm\:mt-0{margin-top:0}.sm\:ml-4{margin-left:1rem}.sm\:ml-10{margin-left:2.5rem}.sm\:ml-6{margin-left:1.5rem}.sm\:mt-16{margin-top:4rem}.sm\:-mt-16{margin-top:-4rem}.sm\:mt-2{margin-top:.5rem}.sm\:-ml-10{margin-left:-2.5rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:inline-grid{display:inline-grid}.sm\:hidden{display:none}.sm\:h-10{height:2.5rem}.sm\:h-12{height:3rem}.sm\:h-32{height:8rem}.sm\:w-10{width:2.5rem}.sm\:w-full{width:100%}.sm\:w-auto{width:auto}.sm\:w-12{width:3rem}.sm\:w-32{width:8rem}.sm\:min-w-0{min-width:0}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-xl{max-width:36rem}.sm\:max-w-2xl{max-width:42rem}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-4xl{max-width:56rem}.sm\:max-w-5xl{max-width:64rem}.sm\:max-w-6xl{max-width:72rem}.sm\:max-w-7xl{max-width:80rem}.sm\:max-w-none{max-width:none}.sm\:flex-1{flex:1 1 0%}.sm\:flex-shrink-0{flex-shrink:0}.sm\:translate-y-0{--tw-translate-y:0px;transform:var(--tw-transform)}.sm\:translate-x-2{--tw-translate-x:0.5rem;transform:var(--tw-transform)}.sm\:translate-x-0{--tw-translate-x:0px;transform:var(--tw-transform)}.sm\:scale-95{--tw-scale-x:.95;--tw-scale-y:.95;transform:var(--tw-transform)}.sm\:scale-100{--tw-scale-x:1;--tw-scale-y:1;transform:var(--tw-transform)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:flex-wrap{flex-wrap:wrap}.sm\:items-start{align-items:flex-start}.sm\:items-end{align-items:flex-end}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-end{justify-content:flex-end}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-8{gap:2rem}.sm\:gap-5{gap:1.25rem}.sm\:gap-4{gap:1rem}.sm\:gap-px{gap:1px}.sm\:gap-6{gap:1.5rem}.sm\:gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}.sm\:space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.25rem*var(--tw-space-x-reverse))}.sm\:space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.sm\:divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.sm\:divide-y-0>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(0px*var(--tw-divide-y-reverse));border-top-width:calc(0px*(1 - var(--tw-divide-y-reverse)))}.sm\:divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(229,231,235,var(--tw-divide-opacity))}.sm\:rounded-lg{border-radius:.5rem}.sm\:rounded-md{border-radius:.375rem}.sm\:rounded-2xl{border-radius:1rem}.sm\:rounded-tl-md{border-top-left-radius:.375rem}.sm\:rounded-tr-md{border-top-right-radius:.375rem}.sm\:rounded-bl-md{border-bottom-left-radius:.375rem}.sm\:rounded-br-md{border-bottom-right-radius:.375rem}.sm\:p-6{padding:1.5rem}.sm\:p-8{padding:2rem}.sm\:p-0{padding:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-20{padding-left:5rem;padding-right:5rem}.sm\:py-12{padding-bottom:3rem;padding-top:3rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:py-24{padding-bottom:6rem;padding-top:6rem}.sm\:py-5{padding-bottom:1.25rem;padding-top:1.25rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-14{padding-left:3.5rem;padding-right:3.5rem}.sm\:pt-0{padding-top:0}.sm\:pb-4{padding-bottom:1rem}.sm\:pt-1{padding-top:.25rem}.sm\:pr-2{padding-right:.5rem}.sm\:pl-6{padding-left:1.5rem}.sm\:pr-6{padding-right:1.5rem}.sm\:pt-20{padding-top:5rem}.sm\:pb-24{padding-bottom:6rem}.sm\:pt-24{padding-top:6rem}.sm\:pb-64{padding-bottom:16rem}.sm\:pb-1{padding-bottom:.25rem}.sm\:pr-4{padding-right:1rem}.sm\:text-left{text-align:left}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-5xl{font-size:3rem;line-height:1}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:duration-700{transition-duration:.7s}}@media (min-width:768px){.md\:fixed{position:fixed}.md\:inset-y-0{bottom:0;top:0}.md\:order-2{order:2}.md\:order-1{order:1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-start-2{grid-column-start:2}.md\:col-start-4{grid-column-start:4}.md\:mt-0{margin-top:0}.md\:-ml-20{margin-left:-5rem}.md\:ml-0{margin-left:0}.md\:ml-6{margin-left:1.5rem}.md\:-mr-16{margin-right:-4rem}.md\:-ml-16{margin-left:-4rem}.md\:ml-2{margin-left:.5rem}.md\:block{display:block}.md\:inline{display:inline}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:h-screen{height:100vh}.md\:w-64{width:16rem}.md\:w-56{width:14rem}.md\:flex-1{flex:1 1 0%}.md\:flex-shrink-0{flex-shrink:0}.md\:flex-grow{flex-grow:1}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-col{flex-direction:column}.md\:items-center{align-items:center}.md\:justify-start{justify-content:flex-start}.md\:justify-center{justify-content:center}.md\:justify-between{justify-content:space-between}.md\:gap-6{gap:1.5rem}.md\:gap-8{gap:2rem}.md\:gap-4{gap:1rem}.md\:space-x-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2.5rem*var(--tw-space-x-reverse))}.md\:overflow-hidden{overflow:hidden}.md\:overflow-y-auto{overflow-y:auto}.md\:rounded-lg{border-radius:.5rem}.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:p-20{padding:5rem}.md\:p-12{padding:3rem}.md\:py-0{padding-bottom:0;padding-top:0}.md\:px-12{padding-left:3rem;padding-right:3rem}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:px-0{padding-left:0;padding-right:0}.md\:pl-20{padding-left:5rem}.md\:pl-64{padding-left:16rem}}@media (min-width:1024px){.lg\:absolute{position:absolute}.lg\:relative{position:relative}.lg\:left-1\/2{left:50%}.lg\:left-0{left:0}.lg\:right-0{right:0}.lg\:bottom-0{bottom:0}.lg\:top-auto{top:auto}.lg\:order-first{order:-9999}.lg\:order-last{order:9999}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-start-2{grid-column-start:2}.lg\:col-start-1{grid-column-start:1}.lg\:m-0{margin:0}.lg\:mx-auto{margin-left:auto;margin-right:auto}.lg\:mx-0{margin-right:0}.lg\:ml-0,.lg\:mx-0{margin-left:0}.lg\:mt-0{margin-top:0}.lg\:mt-16{margin-top:4rem}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:h-full{height:100%}.lg\:h-1{height:.25rem}.lg\:h-48{height:12rem}.lg\:w-0{width:0}.lg\:w-auto{width:auto}.lg\:w-full{width:100%}.lg\:max-w-7xl{max-width:80rem}.lg\:max-w-2xl{max-width:42rem}.lg\:max-w-none{max-width:none}.lg\:flex-1{flex:1 1 0%}.lg\:flex-shrink-0{flex-shrink:0}.lg\:-translate-x-1\/2{--tw-translate-x:-50%;transform:var(--tw-transform)}.lg\:grid-flow-col-dense{grid-auto-flow:column dense}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:items-center{align-items:center}.lg\:justify-between{justify-content:space-between}.lg\:gap-24{gap:6rem}.lg\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.lg\:gap-y-16{row-gap:4rem}.lg\:rounded-none{border-radius:0}.lg\:border-0{border-width:0}.lg\:border-b{border-bottom-width:1px}.lg\:border-l{border-left-width:1px}.lg\:border-r{border-right-width:1px}.lg\:border-gray-200{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-12{padding-bottom:3rem;padding-top:3rem}.lg\:py-16{padding-bottom:4rem;padding-top:4rem}.lg\:px-0{padding-left:0;padding-right:0}.lg\:py-32{padding-bottom:8rem;padding-top:8rem}.lg\:pt-24{padding-top:6rem}.lg\:pl-9{padding-left:2.25rem}.lg\:pl-8{padding-left:2rem}.lg\:pb-8{padding-bottom:2rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}}@media (min-width:1280px){.xl\:absolute{position:absolute}.xl\:relative{position:relative}.xl\:inset-0{left:0;right:0}.xl\:inset-0,.xl\:inset-y-0{bottom:0;top:0}.xl\:top-0{top:0}.xl\:left-0{left:0}.xl\:order-last{order:9999}.xl\:col-span-2{grid-column:span 2/span 2}.xl\:col-start-2{grid-column-start:2}.xl\:col-start-1{grid-column-start:1}.xl\:mt-0{margin-top:0}.xl\:mt-2{margin-top:.5rem}.xl\:grid{display:grid}.xl\:h-full{height:100%}.xl\:w-32{width:8rem}.xl\:grid-flow-col-dense{grid-auto-flow:column dense}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:gap-8{gap:2rem}.xl\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.xl\:overflow-y-auto{overflow-y:auto}.xl\:bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.xl\:px-0{padding-left:0;padding-right:0}.xl\:pb-24{padding-bottom:6rem}}@media (min-width:1536px){.\32xl\:mt-5{margin-top:1.25rem}.\32xl\:block{display:block}.\32xl\:hidden{display:none}} -======= -<<<<<<< HEAD /*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com *//*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ /* @@ -4800,5 +4797,3 @@ body.v-tour--active{pointer-events:none} padding-bottom: 6rem; } } -======= -/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com*//*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */html{-webkit-text-size-adjust:100%;line-height:1.15;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;margin:0}hr{color:inherit;height:0}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}:-moz-focusring{outline:1px dotted ButtonText}:-moz-ui-invalid{box-shadow:none}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:Nunito,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{font-family:inherit;line-height:inherit}*,:after,:before{border:0 solid;box-sizing:border-box}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:-moz-focusring{outline:auto}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{color:inherit;line-height:inherit;padding:0}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-border-opacity:1;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-blur:var(--tw-empty,/*!*/ /*!*/);--tw-brightness:var(--tw-empty,/*!*/ /*!*/);--tw-contrast:var(--tw-empty,/*!*/ /*!*/);--tw-grayscale:var(--tw-empty,/*!*/ /*!*/);--tw-hue-rotate:var(--tw-empty,/*!*/ /*!*/);--tw-invert:var(--tw-empty,/*!*/ /*!*/);--tw-saturate:var(--tw-empty,/*!*/ /*!*/);--tw-sepia:var(--tw-empty,/*!*/ /*!*/);--tw-drop-shadow:var(--tw-empty,/*!*/ /*!*/);--tw-filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);--tw-backdrop-blur:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-brightness:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-contrast:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-grayscale:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-hue-rotate:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-invert:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-opacity:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-saturate:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-sepia:var(--tw-empty,/*!*/ /*!*/);--tw-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);border-color:rgba(229,231,235,var(--tw-border-opacity))}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);outline:2px solid transparent;outline-offset:2px}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}select{-webkit-print-color-adjust:exact;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;color-adjust:exact;padding-right:2.5rem}[multiple]{-webkit-print-color-adjust:unset;background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;color-adjust:unset;padding-right:.75rem}[type=checkbox],[type=radio]{-webkit-print-color-adjust:exact;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;color-adjust:exact;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;width:1rem}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);outline:2px solid transparent;outline-offset:2px}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:transparent}[type=checkbox]:indeterminate{background-color:currentColor;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:transparent}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px auto -webkit-focus-ring-color}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.prose{color:#374151;max-width:65ch}.prose [class~=lead]{color:#4b5563;font-size:1.25em;line-height:1.6;margin-bottom:1.2em;margin-top:1.2em}.prose a{color:#111827;font-weight:500;text-decoration:underline}.prose strong{color:#111827;font-weight:600}.prose ol{counter-reset:list-counter;margin-bottom:1.25em;margin-top:1.25em}.prose ol>li{counter-increment:list-counter;padding-left:1.75em;position:relative}.prose ol>li:before{color:#6b7280;content:counter(list-counter) ".";font-weight:400;left:0;position:absolute}.prose ul>li{padding-left:1.75em;position:relative}.prose ul>li:before{background-color:#d1d5db;border-radius:50%;content:"";height:.375em;left:.25em;position:absolute;top:.6875em;width:.375em}.prose hr{border-color:#e5e7eb;border-top-width:1px;margin-bottom:3em;margin-top:3em}.prose blockquote{border-left-color:#e5e7eb;border-left-width:.25rem;color:#111827;font-style:italic;font-weight:500;margin-bottom:1.6em;margin-top:1.6em;padding-left:1em;quotes:"\201C""\201D""\2018""\2019"}.prose blockquote p:first-of-type:before{content:open-quote}.prose blockquote p:last-of-type:after{content:close-quote}.prose h1{color:#111827;font-size:2.25em;font-weight:800;line-height:1.1111111;margin-bottom:.8888889em;margin-top:0}.prose h2{color:#111827;font-size:1.5em;font-weight:700;line-height:1.3333333;margin-bottom:1em;margin-top:2em}.prose h3{color:#111827;font-size:1.25em;font-weight:600;line-height:1.6;margin-bottom:.6em;margin-top:1.6em}.prose h4{color:#111827;font-weight:600;line-height:1.5;margin-bottom:.5em;margin-top:1.5em}.prose figure figcaption{color:#6b7280;font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose code{color:#111827;font-size:.875em;font-weight:600}.prose code:after,.prose code:before{content:"`"}.prose a code{color:#111827}.prose pre{background-color:#1f2937;border-radius:.375rem;color:#e5e7eb;font-size:.875em;line-height:1.7142857;margin-bottom:1.7142857em;margin-top:1.7142857em;overflow-x:auto;padding:.8571429em 1.1428571em}.prose pre code{background-color:transparent;border-radius:0;border-width:0;color:inherit;font-family:inherit;font-size:inherit;font-weight:400;line-height:inherit;padding:0}.prose pre code:after,.prose pre code:before{content:""}.prose table{font-size:.875em;line-height:1.7142857;margin-bottom:2em;margin-top:2em;table-layout:auto;text-align:left;width:100%}.prose thead{border-bottom-color:#d1d5db;border-bottom-width:1px;color:#111827;font-weight:600}.prose thead th{padding-bottom:.5714286em;padding-left:.5714286em;padding-right:.5714286em;vertical-align:bottom}.prose tbody tr{border-bottom-color:#e5e7eb;border-bottom-width:1px}.prose tbody tr:last-child{border-bottom-width:0}.prose tbody td{padding:.5714286em;vertical-align:top}.prose{font-size:1rem;line-height:1.75}.prose p{margin-bottom:1.25em;margin-top:1.25em}.prose figure,.prose img,.prose video{margin-bottom:2em;margin-top:2em}.prose figure>*{margin-bottom:0;margin-top:0}.prose h2 code{font-size:.875em}.prose h3 code{font-size:.9em}.prose ul{margin-bottom:1.25em;margin-top:1.25em}.prose li{margin-bottom:.5em;margin-top:.5em}.prose>ul>li p{margin-bottom:.75em;margin-top:.75em}.prose>ul>li>:first-child{margin-top:1.25em}.prose>ul>li>:last-child{margin-bottom:1.25em}.prose>ol>li>:first-child{margin-top:1.25em}.prose>ol>li>:last-child{margin-bottom:1.25em}.prose ol ol,.prose ol ul,.prose ul ol,.prose ul ul{margin-bottom:.75em;margin-top:.75em}.prose h2+*,.prose h3+*,.prose h4+*,.prose hr+*{margin-top:0}.prose thead th:first-child{padding-left:0}.prose thead th:last-child{padding-right:0}.prose tbody td:first-child{padding-left:0}.prose tbody td:last-child{padding-right:0}.prose>:first-child{margin-top:0}.prose>:last-child{margin-bottom:0}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:-webkit-sticky;position:sticky}.inset-0{bottom:0;top:0}.inset-0,.inset-x-0{left:0;right:0}.inset-y-0{bottom:0;top:0}.left-0{left:0}.right-0{right:0}.bottom-0{bottom:0}.top-0{top:0}.top-6{top:1.5rem}.right-6{right:1.5rem}.top-3\.5{top:.875rem}.left-4{left:1rem}.top-3{top:.75rem}.top-5{top:1.25rem}.left-5{left:1.25rem}.-bottom-0\.5{bottom:-.125rem}.-right-1{right:-.25rem}.-bottom-0{bottom:0}.top-4{top:1rem}.z-0{z-index:0}.z-50{z-index:50}.z-40{z-index:40}.z-10{z-index:10}.z-30{z-index:30}.col-span-6{grid-column:span 6/span 6}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-12{grid-column:span 12/span 12}.float-right{float:right}.float-left{float:left}.-m-3{margin:-.75rem}.-m-0\.5{margin:-.125rem}.-m-0{margin:0}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-6{margin-bottom:1.5rem;margin-top:1.5rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.-my-2{margin-bottom:-.5rem;margin-top:-.5rem}.my-5{margin-bottom:1.25rem;margin-top:1.25rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.-my-5{margin-bottom:-1.25rem;margin-top:-1.25rem}.-my-4{margin-bottom:-1rem;margin-top:-1rem}.my-7{margin-bottom:1.75rem;margin-top:1.75rem}.my-3{margin-bottom:.75rem;margin-top:.75rem}.my-10{margin-bottom:2.5rem;margin-top:2.5rem}.my-4{margin-bottom:1rem;margin-top:1rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.mx-px{margin-left:1px;margin-right:1px}.ml-3{margin-left:.75rem}.-ml-px{margin-left:-1px}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.-mr-1{margin-right:-.25rem}.mt-3{margin-top:.75rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-1{margin-top:.25rem}.mb-6{margin-bottom:1.5rem}.mr-2{margin-right:.5rem}.mt-8{margin-top:2rem}.ml-4{margin-left:1rem}.ml-12{margin-left:3rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.-mr-0\.5{margin-right:-.125rem}.-mr-0{margin-right:0}.-mr-2{margin-right:-.5rem}.mr-3{margin-right:.75rem}.mt-10{margin-top:2.5rem}.ml-6{margin-left:1.5rem}.mb-4{margin-bottom:1rem}.-mt-px{margin-top:-1px}.mb-5{margin-bottom:1.25rem}.mb-0{margin-bottom:0}.ml-auto{margin-left:auto}.mr-4{margin-right:1rem}.-mr-12{margin-right:-3rem}.ml-1\.5{margin-left:.375rem}.mb-1{margin-bottom:.25rem}.-mt-1\.5{margin-top:-.375rem}.-mt-1{margin-top:-.25rem}.ml-5{margin-left:1.25rem}.-ml-4{margin-left:-1rem}.ml-8{margin-left:2rem}.mt-12{margin-top:3rem}.-mr-48{margin-right:-12rem}.mt-24{margin-top:6rem}.-ml-48{margin-left:-12rem}.-mb-1{margin-bottom:-.25rem}.mr-1\.5{margin-right:.375rem}.mr-1{margin-right:.25rem}.-mt-4{margin-top:-1rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.-ml-1\.5{margin-left:-.375rem}.-ml-1{margin-left:-.25rem}.mt-0\.5{margin-top:.125rem}.mt-0{margin-top:0}.-mt-0\.5{margin-top:-.125rem}.-mt-0{margin-top:0}.-mb-px{margin-bottom:-1px}.ml-3\.5{margin-left:.875rem}.-mb-8{margin-bottom:-2rem}.ml-7{margin-left:1.75rem}.-ml-0\.5{margin-left:-.125rem}.-ml-0{margin-left:0}.mb-24{margin-bottom:6rem}.-mt-12{margin-top:-3rem}.ml-0{margin-left:0}.-ml-8{margin-left:-2rem}.mr-0\.5{margin-right:.125rem}.mr-0{margin-right:0}.mt-px{margin-top:1px}.ml-2\.5{margin-left:.625rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.h-5{height:1.25rem}.h-16{height:4rem}.h-12{height:3rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-4{height:1rem}.h-9{height:2.25rem}.h-10{height:2.5rem}.h-20{height:5rem}.h-full{height:100%}.h-3{height:.75rem}.h-0{height:0}.h-24{height:6rem}.h-80{height:20rem}.h-32{height:8rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-1\.5{height:.375rem}.h-1{height:.25rem}.h-28{height:7rem}.h-7{height:1.75rem}.h-14{height:3.5rem}.max-h-80{max-height:20rem}.max-h-60{max-height:15rem}.max-h-screen{max-height:100vh}.min-h-screen{min-height:100vh}.min-h-0{min-height:0}.w-5{width:1.25rem}.w-16{width:4rem}.w-full{width:100%}.w-0{width:0}.w-12{width:3rem}.w-6{width:1.5rem}.w-3\/4{width:75%}.w-48{width:12rem}.w-auto{width:auto}.w-8{width:2rem}.w-4{width:1rem}.w-60{width:15rem}.w-10{width:2.5rem}.w-1\/2{width:50%}.w-20{width:5rem}.w-64{width:16rem}.w-32{width:8rem}.w-screen{width:100vw}.w-3{width:.75rem}.w-14{width:3.5rem}.w-24{width:6rem}.w-56{width:14rem}.w-2{width:.5rem}.w-96{width:24rem}.w-1\/3{width:33.333333%}.w-2\.5{width:.625rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-0\.5{width:.125rem}.w-9{width:2.25rem}.w-28{width:7rem}.w-11{width:2.75rem}.w-72{width:18rem}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-screen-xl{max-width:1280px}.max-w-7xl{max-width:80rem}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.max-w-xs{max-width:20rem}.max-w-4xl{max-width:56rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-3xl{max-width:48rem}.max-w-2xl{max-width:42rem}.max-w-5xl{max-width:64rem}.max-w-prose{max-width:65ch}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.flex-none{flex:none}.flex-auto{flex:1 1 auto}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.table-fixed{table-layout:fixed}.origin-top-left{transform-origin:top left}.origin-top{transform-origin:top}.origin-top-right{transform-origin:top right}.translate-y-4{--tw-translate-y:1rem;transform:var(--tw-transform)}.translate-y-0{--tw-translate-y:0px;transform:var(--tw-transform)}.-translate-x-full{--tw-translate-x:-100%;transform:var(--tw-transform)}.translate-x-full{--tw-translate-x:100%;transform:var(--tw-transform)}.translate-x-0{--tw-translate-x:0px;transform:var(--tw-transform)}.translate-y-1{--tw-translate-y:0.25rem;transform:var(--tw-transform)}.translate-y-2{--tw-translate-y:0.5rem;transform:var(--tw-transform)}.translate-x-5{--tw-translate-x:1.25rem;transform:var(--tw-transform)}.rotate-90{--tw-rotate:90deg}.rotate-90,.scale-95{transform:var(--tw-transform)}.scale-95{--tw-scale-x:.95;--tw-scale-y:.95}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-100,.transform{transform:var(--tw-transform)}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}.animate-pulse{-webkit-animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite;animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.content-center{align-content:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-6{gap:1.5rem}.gap-4{gap:1rem}.gap-1{gap:.25rem}.gap-8{gap:2rem}.gap-7{gap:1.75rem}.gap-2{gap:.5rem}.gap-5{gap:1.25rem}.gap-3{gap:.75rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-12{row-gap:3rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-8{row-gap:2rem}.gap-y-4{row-gap:1rem}.gap-y-6{row-gap:1.5rem}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.5rem*var(--tw-space-y-reverse));margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-x-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2.5rem*var(--tw-space-x-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.25rem*var(--tw-space-x-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-y-2>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(2px*var(--tw-divide-y-reverse));border-top-width:calc(2px*(1 - var(--tw-divide-y-reverse)))}.divide-x>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;border-left-width:calc(1px*(1 - var(--tw-divide-x-reverse)));border-right-width:calc(1px*var(--tw-divide-x-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(229,231,235,var(--tw-divide-opacity))}.divide-gray-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(249,250,251,var(--tw-divide-opacity))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(243,244,246,var(--tw-divide-opacity))}.divide-gray-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(107,114,128,var(--tw-divide-opacity))}.divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(209,213,219,var(--tw-divide-opacity))}.divide-gray-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(75,85,99,var(--tw-divide-opacity))}.divide-opacity-10>:not([hidden])~:not([hidden]){--tw-divide-opacity:0.1}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.break-normal{overflow-wrap:normal;word-break:normal}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded-md{border-radius:.375rem}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-xl{border-radius:.75rem}.rounded-sm{border-radius:.125rem}.rounded-none{border-radius:0}.rounded-l-md{border-bottom-left-radius:.375rem;border-top-left-radius:.375rem}.rounded-r-md{border-bottom-right-radius:.375rem;border-top-right-radius:.375rem}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.rounded-b-none{border-bottom-left-radius:0;border-bottom-right-radius:0}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.rounded-b-md{border-bottom-left-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-l-none{border-bottom-left-radius:0;border-top-left-radius:0}.rounded-br-lg{border-bottom-right-radius:.5rem}.rounded-tl{border-top-left-radius:.25rem}.border{border-width:1px}.border-2{border-width:2px}.border-0{border-width:0}.border-b-2{border-bottom-width:2px}.border-l-4{border-left-width:4px}.border-t{border-top-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-r-4{border-right-width:4px}.border-l{border-left-width:1px}.border-b-0{border-bottom-width:0}.border-t-0{border-top-width:0}.border-dashed{border-style:dashed}.border-gray-300{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-indigo-400{--tw-border-opacity:1;border-color:rgba(129,140,248,var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgba(243,244,246,var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgba(248,113,113,var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.border-teal-600{--tw-border-opacity:1;border-color:rgba(0,136,160,var(--tw-border-opacity))}.border-teal-500{--tw-border-opacity:1;border-color:rgba(1,157,187,var(--tw-border-opacity))}.border-pink-500{--tw-border-opacity:1;border-color:rgba(236,72,153,var(--tw-border-opacity))}.border-r-black{--tw-border-opacity:1;border-right-color:rgba(0,0,0,var(--tw-border-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgba(99,102,241,var(--tw-bg-opacity))}.bg-red-700{--tw-bg-opacity:1;background-color:rgba(185,28,28,var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgba(107,114,128,var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgba(79,70,229,var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgba(31,41,55,var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgba(254,226,226,var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgba(238,242,255,var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity:1;background-color:rgba(16,185,129,var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgba(254,243,199,var(--tw-bg-opacity))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgba(251,191,36,var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgba(75,85,99,var(--tw-bg-opacity))}.bg-teal-600{--tw-bg-opacity:1;background-color:rgba(0,136,160,var(--tw-bg-opacity))}.bg-pink-500{--tw-bg-opacity:1;background-color:rgba(236,72,153,var(--tw-bg-opacity))}.bg-purple-500{--tw-bg-opacity:1;background-color:rgba(139,92,246,var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgba(245,158,11,var(--tw-bg-opacity))}.bg-teal-500{--tw-bg-opacity:1;background-color:rgba(1,157,187,var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgba(17,24,39,var(--tw-bg-opacity))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgba(240,249,255,var(--tw-bg-opacity))}.bg-sky-400{--tw-bg-opacity:1;background-color:rgba(56,189,248,var(--tw-bg-opacity))}.bg-sky-500{--tw-bg-opacity:1;background-color:rgba(14,165,233,var(--tw-bg-opacity))}.bg-indigo-900{--tw-bg-opacity:1;background-color:rgba(49,46,129,var(--tw-bg-opacity))}.bg-indigo-800{--tw-bg-opacity:1;background-color:rgba(55,48,163,var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-blue-100{--tw-bg-opacity:1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.bg-rose-500{--tw-bg-opacity:1;background-color:rgba(244,63,94,var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgba(156,163,175,var(--tw-bg-opacity))}.bg-green-200{--tw-bg-opacity:1;background-color:rgba(167,243,208,var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgba(209,250,229,var(--tw-bg-opacity))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgba(224,231,255,var(--tw-bg-opacity))}.bg-green-600{--tw-bg-opacity:1;background-color:rgba(5,150,105,var(--tw-bg-opacity))}.bg-purple-400{--tw-bg-opacity:1;background-color:rgba(167,139,250,var(--tw-bg-opacity))}.bg-opacity-25{--tw-bg-opacity:0.25}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-opacity-10{--tw-bg-opacity:0.1}.bg-opacity-80{--tw-bg-opacity:0.8}.bg-opacity-5{--tw-bg-opacity:0.05}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-tl{background-image:linear-gradient(to top left,var(--tw-gradient-stops))}.from-indigo-600{--tw-gradient-from:#4f46e5;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(79,70,229,0))}.from-indigo-800{--tw-gradient-from:#3730a3;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(55,48,163,0))}.from-gray-900{--tw-gradient-from:#111827;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(17,24,39,0))}.from-indigo-300{--tw-gradient-from:#a5b4fc;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(165,180,252,0))}.from-sky-400{--tw-gradient-from:#38bdf8;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(56,189,248,0))}.to-teal-600{--tw-gradient-to:#0088a0}.to-teal-300,.to-teal-700{--tw-gradient-to:#019dbb}.bg-cover{background-size:cover}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.bg-center{background-position:50%}.bg-no-repeat{background-repeat:no-repeat}.bg-origin-border{background-origin:border-box}.fill-current{fill:currentColor}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-6{padding:1.5rem}.p-1{padding:.25rem}.p-0\.5{padding:.125rem}.p-0{padding:0}.p-4{padding:1rem}.p-3{padding:.75rem}.p-12{padding:3rem}.p-px{padding:1px}.p-8{padding:2rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.py-8{padding-bottom:2rem;padding-top:2rem}.py-12{padding-bottom:3rem;padding-top:3rem}.py-10{padding-bottom:2.5rem;padding-top:2.5rem}.px-0{padding-left:0;padding-right:0}.px-7{padding-left:1.75rem;padding-right:1.75rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.px-12{padding-left:3rem;padding-right:3rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-16{padding-bottom:4rem;padding-top:4rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-0{padding-bottom:0;padding-top:0}.px-8{padding-left:2rem;padding-right:2rem}.py-2\.5{padding-bottom:.625rem;padding-top:.625rem}.py-14{padding-bottom:3.5rem;padding-top:3.5rem}.py-3\.5{padding-bottom:.875rem;padding-top:.875rem}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.py-px{padding-bottom:1px;padding-top:1px}.pt-6{padding-top:1.5rem}.pt-5{padding-top:1.25rem}.pb-4{padding-bottom:1rem}.pt-1{padding-top:.25rem}.pl-3{padding-left:.75rem}.pr-4{padding-right:1rem}.pt-2{padding-top:.5rem}.pb-3{padding-bottom:.75rem}.pt-4{padding-top:1rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pr-5{padding-right:1.25rem}.pt-8{padding-top:2rem}.pt-0{padding-top:0}.pr-1{padding-right:.25rem}.pb-0{padding-bottom:0}.pl-2{padding-left:.5rem}.pb-5{padding-bottom:1.25rem}.pl-8{padding-left:2rem}.pr-3{padding-right:.75rem}.pb-6{padding-bottom:1.5rem}.pt-16{padding-top:4rem}.pb-32{padding-bottom:8rem}.pl-4{padding-left:1rem}.pt-12{padding-top:3rem}.pb-64{padding-bottom:16rem}.pb-8{padding-bottom:2rem}.pt-3{padding-top:.75rem}.pr-2{padding-right:.5rem}.pl-7{padding-left:1.75rem}.pt-0\.5{padding-top:.125rem}.pl-11{padding-left:2.75rem}.pr-10{padding-right:2.5rem}.pl-1\.5{padding-left:.375rem}.pl-1{padding-left:.25rem}.pl-5{padding-left:1.25rem}.pb-16{padding-bottom:4rem}.pl-10{padding-left:2.5rem}.pr-20{padding-right:5rem}.pb-12{padding-bottom:3rem}.pt-1\.5{padding-top:.375rem}.pl-16{padding-left:4rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-justify{text-align:justify}.font-sans{font-family:Nunito,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-base{font-size:1rem;line-height:1.5rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-normal{font-weight:400}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-5{line-height:1.25rem}.leading-7{line-height:1.75rem}.leading-tight{line-height:1.25}.leading-4{line-height:1rem}.leading-6{line-height:1.5rem}.leading-8{line-height:2rem}.leading-none{line-height:1}.tracking-widest{letter-spacing:.1em}.tracking-wider{letter-spacing:.05em}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.text-gray-500{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgba(75,85,99,var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgba(79,70,229,var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgba(220,38,38,var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgba(17,24,39,var(--tw-text-opacity))}.text-indigo-700{--tw-text-opacity:1;color:rgba(67,56,202,var(--tw-text-opacity))}.text-green-400{--tw-text-opacity:1;color:rgba(52,211,153,var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgba(156,163,175,var(--tw-text-opacity))}.text-indigo-500{--tw-text-opacity:1;color:rgba(99,102,241,var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgba(239,68,68,var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgba(5,150,105,var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgba(16,185,129,var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgba(146,64,14,var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgba(229,231,235,var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgba(209,213,219,var(--tw-text-opacity))}.text-sky-500{--tw-text-opacity:1;color:rgba(14,165,233,var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgba(185,28,28,var(--tw-text-opacity))}.text-teal-200,.text-teal-700{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.text-indigo-200{--tw-text-opacity:1;color:rgba(199,210,254,var(--tw-text-opacity))}.text-transparent{color:transparent}.text-teal-800{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.text-sky-700{--tw-text-opacity:1;color:rgba(3,105,161,var(--tw-text-opacity))}.text-red-400{--tw-text-opacity:1;color:rgba(248,113,113,var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgba(243,244,246,var(--tw-text-opacity))}.text-indigo-300{--tw-text-opacity:1;color:rgba(165,180,252,var(--tw-text-opacity))}.text-teal-600{--tw-text-opacity:1;color:rgba(0,136,160,var(--tw-text-opacity))}.text-green-800{--tw-text-opacity:1;color:rgba(6,95,70,var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity:1;color:rgba(251,191,36,var(--tw-text-opacity))}.text-indigo-800{--tw-text-opacity:1;color:rgba(55,48,163,var(--tw-text-opacity))}.text-indigo-400{--tw-text-opacity:1;color:rgba(129,140,248,var(--tw-text-opacity))}.text-teal-500{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.text-green-200{--tw-text-opacity:1;color:rgba(167,243,208,var(--tw-text-opacity))}.text-opacity-40{--tw-text-opacity:0.4}.text-opacity-100{--tw-text-opacity:1}.underline{text-decoration:underline}.no-underline{text-decoration:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgba(107,114,128,var(--tw-placeholder-opacity))}.placeholder-gray-500:-ms-input-placeholder{--tw-placeholder-opacity:1;color:rgba(107,114,128,var(--tw-placeholder-opacity))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgba(107,114,128,var(--tw-placeholder-opacity))}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-75{opacity:.75}.opacity-50{opacity:.5}.opacity-25{opacity:.25}.opacity-60{opacity:.6}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,0.05)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,0.1),0 1px 2px 0 rgba(0,0,0,0.06)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,0.1),0 2px 4px -1px rgba(0,0,0,0.06)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,0.1),0 10px 10px -5px rgba(0,0,0,0.04)}.shadow-2xl,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,0.25)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-1,.ring-4{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-4{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-8{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-0,.ring-8{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgba(209,213,219,var(--tw-ring-opacity))}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgba(0,0,0,var(--tw-ring-opacity))}.ring-white{--tw-ring-opacity:1;--tw-ring-color:rgba(255,255,255,var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.filter{filter:var(--tw-filter)}.backdrop-blur{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-filter);backdrop-filter:var(--tw-backdrop-filter)}.transition{transition-duration:.15s;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:background-color,border-color,color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.delay-150{transition-delay:.15s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-75{transition-duration:75ms}.duration-300{transition-duration:.3s}.duration-1000{transition-duration:1s}.duration-100{transition-duration:.1s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-linear{transition-timing-function:linear}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-2,.line-clamp-4{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-4{-webkit-line-clamp:4}body.v-tour--active{pointer-events:none}.v-tour{pointer-events:auto}.v-tour__target--highlighted{box-shadow:0 0 0 4px rgba(0,0,0,.4);pointer-events:auto;z-index:9999}.v-tour__target--relative{position:relative}.v-step[data-v-c1b3a65a]{background:#50596c;border-radius:3px;color:#fff;filter:drop-shadow(0 0 2px rgba(0,0,0,.5));max-width:320px;padding:1rem;text-align:center;z-index:10000}.v-step .v-step__arrow[data-v-c1b3a65a]{border-color:#50596c;border-style:solid;height:0;margin:.5rem;position:absolute;width:0}.v-step .v-step__arrow--dark[data-v-c1b3a65a]{border-color:#454d5d}.v-step[x-placement^=top][data-v-c1b3a65a]{margin-bottom:.5rem}.v-step[x-placement^=top] .v-step__arrow[data-v-c1b3a65a]{border-bottom-color:transparent;border-left-color:transparent;border-right-color:transparent;border-width:.5rem .5rem 0;bottom:-.5rem;left:calc(50% - 1rem);margin-bottom:0;margin-top:0}.v-step[x-placement^=bottom][data-v-c1b3a65a]{margin-top:.5rem}.v-step[x-placement^=bottom] .v-step__arrow[data-v-c1b3a65a]{border-left-color:transparent;border-right-color:transparent;border-top-color:transparent;border-width:0 .5rem .5rem;left:calc(50% - 1rem);margin-bottom:0;margin-top:0;top:-.5rem}.v-step[x-placement^=right][data-v-c1b3a65a]{margin-left:.5rem}.v-step[x-placement^=right] .v-step__arrow[data-v-c1b3a65a]{border-bottom-color:transparent;border-left-color:transparent;border-top-color:transparent;border-width:.5rem .5rem .5rem 0;left:-.5rem;margin-left:0;margin-right:0;top:calc(50% - 1rem)}.v-step[x-placement^=left][data-v-c1b3a65a]{margin-right:.5rem}.v-step[x-placement^=left] .v-step__arrow[data-v-c1b3a65a]{border-bottom-color:transparent;border-right-color:transparent;border-top-color:transparent;border-width:.5rem 0 .5rem .5rem;margin-left:0;margin-right:0;right:-.5rem;top:calc(50% - 1rem)}.v-step__header[data-v-c1b3a65a]{background-color:#454d5d;border-top-left-radius:3px;border-top-right-radius:3px;margin:-1rem -1rem .5rem;padding:.5rem}.v-step__content[data-v-c1b3a65a]{margin:0 0 1rem}.v-step__button[data-v-c1b3a65a]{background:0 0;border:.05rem solid #fff;border-radius:.1rem;color:#fff;cursor:pointer;display:inline-block;font-size:.8rem;height:1.8rem;line-height:1rem;margin:0 .2rem;outline:0;padding:.35rem .4rem;text-align:center;text-decoration:none;transition:all .2s ease;vertical-align:middle;white-space:nowrap}.v-step__button[data-v-c1b3a65a]:hover{background-color:rgba(255,255,255,.95);color:#50596c}.mask[data-v-c1b3a65a]{background:rgba(0,0,0,.5);bottom:0;left:0;position:absolute;right:0;top:0}.mask .tour-focus-container[data-v-c1b3a65a]{border-radius:4px;box-shadow:0 0 0 9999px rgba(17,55,80,.4),0 0 15px rgba(0,0,0,.5);height:100%;left:0;opacity:1;pointer-events:auto;position:absolute;top:0;transition:opacity .2s;width:100%}.tooltip{display:inline-block;position:relative}.tooltip .tooltiptext{bottom:100%;left:50%;margin-left:-60px;position:absolute;visibility:hidden;z-index:1}.tooltip:hover .tooltiptext{visibility:visible}.vue-tags-input{max-width:100%!important}.vue-tags-input .ti-new-tag-input{background:transparent}.vue-tags-input .ti-input{border-radius:4px;max-width:none;padding:4px 10px}.vue-tags-input.ti-focus .ti-input{--tw-shadow:0 1px 2px 0 rgba(0,0,0,0.05);border-color:rgba(209,213,219,var(--tw-border-opacity));border-width:1px;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ti-tag[data-v-2fbda277]{border-radius:4px!important;font-size:1em!important}.ti-new-tag-input-wrapper input:focus{border-color:transparent;box-shadow:none!important;outline:none!important;outline:0 solid transparent;outline-offset:0}.index_beams{background-color:#fff;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600'%3E%3Cpath fill='%23009ebb' fill-opacity='.08' d='M600 325.1v-1.17c-6.5 3.83-13.06 7.64-14.68 8.64-10.6 6.56-18.57 12.56-24.68 19.09-5.58 5.95-12.44 10.06-22.42 14.15-1.45.6-2.96 1.2-4.83 1.9l-4.75 1.82c-9.78 3.75-14.8 6.27-18.98 10.1-4.23 3.88-9.65 6.6-16.77 8.84-1.95.6-3.99 1.17-6.47 1.8l-6.14 1.53c-5.29 1.35-8.3 2.37-10.54 3.78-3.08 1.92-6.63 3.26-12.74 5.03a384.1 384.1 0 0 1-4.82 1.36c-2.04.58-3.6 1.04-5.17 1.52a110.03 110.03 0 0 0-11.2 4.05c-2.7 1.15-5.5 3.93-8.78 8.4a157.68 157.68 0 0 0-6.15 9.2c-5.75 9.07-7.58 11.74-10.24 14.51a50.97 50.97 0 0 1-4.6 4.22c-2.33 1.9-10.39 7.54-11.81 8.74a14.68 14.68 0 0 0-3.67 4.15c-1.24 2.3-1.9 4.57-2.78 8.87-2.17 10.61-3.52 14.81-8.2 22.1-4.07 6.33-6.8 9.88-9.83 12.99-.47.48-.95.96-1.5 1.48l-3.75 3.56c-1.67 1.6-3.18 3.12-4.86 4.9a42.44 42.44 0 0 0-9.89 16.94c-2.5 8.13-2.72 15.47-1.76 27.22.47 5.82.51 6.36.51 8.18 0 10.51.12 17.53.63 25.78.24 4.05.56 7.8.97 11.22h.9c-1.13-9.58-1.5-21.83-1.5-37 0-1.86-.04-2.4-.52-8.26-.94-11.63-.72-18.87 1.73-26.85a41.44 41.44 0 0 1 9.65-16.55c1.67-1.76 3.18-3.27 4.83-4.85.63-.6 3.13-2.96 3.75-3.57a71.6 71.6 0 0 0 1.52-1.5c3.09-3.16 5.86-6.76 9.96-13.15 4.77-7.42 6.15-11.71 8.34-22.44.86-4.21 1.5-6.4 2.68-8.6.68-1.25 1.79-2.48 3.43-3.86 1.38-1.15 9.43-6.8 11.8-8.72 1.71-1.4 3.26-2.81 4.7-4.3 2.72-2.85 4.56-5.54 10.36-14.67a156.9 156.9 0 0 1 6.1-9.15c3.2-4.33 5.9-7.01 8.37-8.07 3.5-1.5 7.06-2.77 11.1-4.02a233.84 233.84 0 0 1 7.6-2.2l2.38-.67c6.19-1.79 9.81-3.16 12.98-5.15 2.14-1.33 5.08-2.33 10.27-3.65l6.14-1.53c2.5-.63 4.55-1.2 6.52-1.82 7.24-2.27 12.79-5.06 17.15-9.05 4.05-3.72 9-6.2 18.66-9.9l4.75-1.82c1.87-.72 3.39-1.31 4.85-1.91 10.1-4.15 17.07-8.32 22.76-14.4 6.05-6.45 13.95-12.4 24.49-18.92 1.56-.96 7.82-4.6 14.15-8.33v-64.58c-4 8.15-8.52 14.85-12.7 17.9-2.51 1.82-5.38 4.02-9.04 6.92a1063.87 1063.87 0 0 0-6.23 4.98l-1.27 1.02a2309.25 2309.25 0 0 1-4.87 3.9c-7.55 6-12.9 10.05-17.61 13.19-3.1 2.06-3.86 2.78-8.06 7.13-5.84 6.07-11.72 8.62-29.15 10.95-11.3 1.5-20.04 4.91-30.75 11.07-1.65.94-7.27 4.27-6.97 4.1-2.7 1.58-4.69 2.69-6.64 3.66-5.63 2.8-10.47 4.17-15.71 4.17-17.13 0-41.44 11.51-51.63 22.83-12.05 13.4-31.42 27.7-45.25 31.16-7.4 1.85-11.85 7.05-14.04 14.69-1.26 4.4-1.58 8.28-1.58 13.82 0 .82.01.98.24 3.63.45 5.18.35 8.72-.77 13.26-1.53 6.2-4.89 12.6-10.59 19.43-13.87 16.65-22.88 46.58-22.88 71.68 0 2.39.02 4.26.06 8.75.12 10.8.1 15.8-.22 21.95-.56 11.18-2.09 20.73-5 29.3h-1.05c2.94-8.56 4.49-18.12 5.05-29.35.31-6.13.34-11.1.22-21.9-.04-4.48-.06-6.36-.06-8.75 0-25.32 9.07-55.47 23.12-72.32 5.6-6.72 8.88-12.99 10.38-19.03 1.09-4.4 1.18-7.85.74-12.93-.23-2.7-.24-2.86-.24-3.72 0-5.62.32-9.57 1.62-14.1 2.28-7.95 6.97-13.44 14.76-15.39 13.6-3.4 32.82-17.59 44.75-30.84C409 360.14 433.58 348.5 451 348.5c5.07 0 9.77-1.33 15.26-4.07 1.93-.96 3.9-2.05 6.58-3.62-.3.18 5.33-3.16 6.98-4.11 10.82-6.21 19.66-9.67 31.11-11.2 17.23-2.3 22.9-4.75 28.57-10.64 4.25-4.41 5.04-5.16 8.22-7.28 4.68-3.11 10.01-7.14 17.55-13.14a1113.33 1113.33 0 0 0 4.86-3.89l1.28-1.02a4668.54 4668.54 0 0 1 6.23-4.98c3.67-2.9 6.55-5.12 9.07-6.95 4.37-3.19 9.16-10.56 13.29-19.4v66.9zm0-116.23c-.62.01-1.27.06-1.95.13-6.13.63-13.83 3.45-21.83 7.45-3.64 1.82-8.46 2.67-14.17 2.71-4.7.04-9.72-.47-14.73-1.33-1.7-.3-3.26-.61-4.67-.93a31.55 31.55 0 0 0-3.55-.57 273.4 273.4 0 0 0-16.66-.88c-10.42-.16-17.2.74-17.97 2.73-.38.97.6 2.55 3.03 4.87 1.01.97 2.22 2.03 4.04 3.55a1746.07 1746.07 0 0 0 4.79 4.02c1.39 1.2 3.1 1.92 5.5 2.5.7.16.86.2 2.64.54 3.53.7 5.03 1.25 6.15 2.63 1.41 1.76 1.4 4.54-.15 8.88-2.44 6.83-5.72 10.05-10.19 10.33-3.63.23-7.6-1.29-14.52-5.06-4.53-2.47-6.82-7.3-8.32-15.26-.17-.87-.32-1.78-.5-2.86l-.43-2.76c-1.05-6.58-1.9-9.2-3.73-10.11-.81-.4-1.59-.74-2.36-1-2.27-.77-4.6-1.02-8.1-.92-2.29.07-14.7 1-13.77.93-20.55 1.37-28.8 5.05-37.09 14.99a133.07 133.07 0 0 0-4.25 5.44l-2.3 3.09-2.51 3.32c-4.1 5.36-7.06 8.48-10.39 11.12-.65.52-1.33 1.04-2.13 1.62l-4.11 2.94a106.8 106.8 0 0 0-5.16 3.99c-4.55 3.74-9.74 8.6-16.25 15.38-8.25 8.58-11.78 13.54-11.7 15.95.07 1.65 1.64 2.11 6.79 2.38 1.61.09 2.15.12 2.98.2 2.95.24 5.09.73 6.81 1.68 7.48 4.15 11.63 7.26 13.95 11.58 3.3 6.15.8 12.88-8.89 20.26-8.28 6.3-11.1 10.37-11.31 14.96-.06 1.17 0 1.93.26 4.43.69 6.47.25 10.65-2.8 17.42a44.23 44.23 0 0 1-4.16 7.53c-2.82 3.97-5.47 5.74-10.6 7.69-.43.16-3.34 1.23-4.27 1.59-1.8.68-3.38 1.36-5.01 2.14-4.18 2-8.4 4.6-13.1 8.24-8.44 6.51-13.23 14.56-15.98 25.06-1.1 4.2-1.55 6.81-2.8 15.21-1.26 8.6-2.17 12.64-4.08 16.55-2.1 4.28-11.93 26.59-12.97 28.88a382.7 382.7 0 0 1-6.37 13.41c-4.07 8.11-7.61 14.07-10.73 17.81-5.38 6.46-8.98 14.37-13.77 28.42a810.14 810.14 0 0 0-1.89 5.6c-1.8 5.35-2.96 8.6-4.26 11.85-6.13 15.32-25.43 26.31-46.46 26.31-11.2 0-20.58-2.74-31.02-8.55-5.6-3.13-4.55-2.42-22.26-14.54-14.33-9.8-17.7-10.73-20.47-6.9-.37.5-1.81 2.74-1.83 2.77a52.24 52.24 0 0 1-4.94 5.9c-.73.79-5.52 5.87-6.97 7.45-2.38 2.6-4.3 4.81-5.98 6.93a45.6 45.6 0 0 0-5.08 7.66c-1.29 2.57-1.9 5.25-2.66 10.6a997.6 997.6 0 0 1-.46 3.18h-1l.47-3.32c.77-5.45 1.4-8.2 2.75-10.9a46.54 46.54 0 0 1 5.2-7.84c1.7-2.14 3.63-4.38 6.03-6.98 1.45-1.59 6.24-6.68 6.96-7.46a51.58 51.58 0 0 0 4.84-5.78s1.47-2.26 1.86-2.8c3.25-4.5 7.08-3.44 21.84 6.67 17.67 12.08 16.62 11.38 22.19 14.48 10.3 5.73 19.5 8.43 30.53 8.43 20.65 0 39.57-10.77 45.54-25.69a219.7 219.7 0 0 0 4.24-11.8 6752.32 6752.32 0 0 0 1.88-5.6c4.83-14.16 8.47-22.14 13.96-28.73 3.05-3.66 6.56-9.57 10.6-17.61 1.97-3.93 4.04-8.31 6.35-13.38 1.03-2.28 10.88-24.61 12.98-28.91 1.85-3.79 2.75-7.76 4-16.25 1.24-8.44 1.7-11.07 2.81-15.32 2.8-10.7 7.71-18.94 16.33-25.6a73.18 73.18 0 0 1 13.29-8.35c1.66-.8 3.27-1.48 5.08-2.18.94-.36 3.86-1.43 4.28-1.59 4.95-1.88 7.44-3.55 10.14-7.33 1.35-1.9 2.68-4.3 4.06-7.37 2.97-6.58 3.39-10.59 2.72-16.9a27.13 27.13 0 0 1-.27-4.58c.22-4.94 3.21-9.24 11.7-15.7 9.33-7.11 11.66-13.34 8.62-19-2.2-4.09-6.25-7.12-13.55-11.17-1.57-.88-3.6-1.33-6.42-1.57-.8-.07-1.34-.1-2.95-.19-5.77-.3-7.63-.85-7.72-3.34-.1-2.81 3.5-7.87 11.97-16.69 6.53-6.8 11.75-11.69 16.33-15.45 1.79-1.47 3.42-2.72 5.2-4.03l4.12-2.94c.79-.58 1.46-1.08 2.1-1.59 3.26-2.6 6.16-5.65 10.21-10.94a383.2 383.2 0 0 0 2.5-3.32l2.31-3.09c1.8-2.39 3.04-4 4.29-5.48 8.47-10.17 16.98-13.96 37.27-15.3-.44.02 12-.9 14.32-.98 3.62-.1 6.05.16 8.46.98.8.27 1.62.62 2.47 1.04 2.27 1.14 3.17 3.87 4.27 10.85l.44 2.76c.17 1.07.33 1.97.5 2.83 1.44 7.69 3.62 12.29 7.8 14.57 6.76 3.68 10.6 5.15 13.99 4.94 4-.25 6.99-3.17 9.3-9.67 1.45-4.04 1.46-6.49.32-7.92-.9-1.12-2.28-1.62-5.57-2.27a55.8 55.8 0 0 1-2.67-.55c-2.54-.6-4.39-1.4-5.93-2.71a252.63 252.63 0 0 0-4.78-4.01 84.35 84.35 0 0 1-4.08-3.6c-2.73-2.6-3.86-4.43-3.28-5.95 1.02-2.64 7.82-3.54 18.93-3.37a230.56 230.56 0 0 1 16.73.88c2.76.39 3.2.49 3.68.6 1.4.3 2.95.62 4.62.91a82.9 82.9 0 0 0 14.56 1.32c5.56-.04 10.24-.86 13.73-2.6 8.1-4.05 15.89-6.9 22.17-7.56.7-.07 1.4-.11 2.05-.13v1zm0-100.94v1.5c-8.62 16.05-17.27 29.55-23.65 35.92-3.19 3.2-7.62 4.9-13.54 5.56-4.45.48-8.28.4-19.18-.2-9.91-.55-15.32-.44-20.52.78a84.05 84.05 0 0 1-15 2.11l-2.25.14c-12.49.75-19.37 1.78-32.72 5.74-4.5 1.33-9.27 2.49-14.3 3.48a246.27 246.27 0 0 1-32.6 3.97c-7.56.45-13.21.57-20.24.57-5.4 0-11.9 1.61-18 5.18-8.3 4.87-15.06 12.87-19.53 24.5a68.57 68.57 0 0 1-4.56 9.8c-3.6 6.2-6.92 8.99-13.38 12.18l-4.03 1.96a64.48 64.48 0 0 0-15.16 10.25c-8.2 7.33-13.72 16.63-22.54 35.6l-2.08 4.49c-7.3 15.7-11.5 23.3-17.35 29.87-7.7 8.66-20.25 14.42-40.31 20.08-4.37 1.23-19.04 5.08-19.24 5.13-6.92 1.87-11.68 3.34-15.63 4.92-10.55 4.22-18.71 10.52-36.38 26.52l-1.7 1.54c-8.58 7.76-13.41 11.9-18.81 15.88-3.95 2.9-8 5.67-12.97 8.91-2.06 1.34-10.3 6.6-12.33 7.94-11.52 7.5-18.53 13.04-24.62 20.08a62.01 62.01 0 0 0-6.44 8.85c-4.13 6.91-6.27 13.15-9.2 25.11l-1.54 6.26c-.6 2.45-1.15 4.54-1.72 6.58-2.97 10.7-6.9 17.36-14.78 26.91L69.6 491a148.51 148.51 0 0 0-4.19 5.3 23.9 23.9 0 0 0-3.44 6.28c-1.16 3.23-1.52 5.9-1.87 11.94-.58 10.05-1.42 15.04-4.63 22.67-1.57 3.72-5.66 14.02-6.41 15.8a73.46 73.46 0 0 1-3.57 7.4c-2.88 5.14-6.71 10.12-13.12 16.95-5.96 6.36-8.87 10.9-10.61 16a56.88 56.88 0 0 0-1.38 4.82l-.46 1.84h-1.03l.52-2.08c.52-2.09.92-3.49 1.4-4.9 1.8-5.25 4.78-9.9 10.84-16.36 6.35-6.78 10.13-11.7 12.97-16.77a72.5 72.5 0 0 0 3.52-7.29c.75-1.76 4.84-12.06 6.4-15.8 3.17-7.5 3.99-12.4 4.56-22.33.35-6.14.72-8.88 1.93-12.23a24.9 24.9 0 0 1 3.58-6.54c1.27-1.7 2.6-3.37 4.22-5.34l4.11-4.95c7.8-9.46 11.66-16 14.59-26.54.56-2.04 1.1-4.12 1.71-6.56l1.53-6.26c2.96-12.04 5.13-18.36 9.32-25.39 1.84-3.08 4-6.05 6.54-8.99 6.17-7.12 13.24-12.7 24.83-20.26 2.05-1.33 10.28-6.6 12.33-7.94 4.96-3.22 9-5.98 12.92-8.87 5.37-3.95 10.19-8.08 18.74-15.82l1.7-1.54c17.76-16.09 25.98-22.43 36.67-26.7 4-1.6 8.8-3.09 15.75-4.96.21-.06 14.87-3.9 19.22-5.13 19.9-5.61 32.32-11.31 39.85-19.78 5.76-6.48 9.93-14.02 17.18-29.64l2.09-4.5c8.87-19.07 14.44-28.46 22.77-35.9a65.48 65.48 0 0 1 15.38-10.4l4.04-1.97c6.3-3.1 9.47-5.77 12.96-11.77a67.6 67.6 0 0 0 4.48-9.67c4.56-11.84 11.47-20.02 19.97-25 6.25-3.66 12.93-5.32 18.5-5.32 7.01 0 12.65-.12 20.17-.57a245.3 245.3 0 0 0 32.47-3.96c5-.98 9.75-2.13 14.22-3.45 13.43-3.98 20.38-5.02 32.94-5.78l2.24-.14c5.76-.37 9.8-.9 14.85-2.09 5.31-1.25 10.79-1.35 22.6-.7 9.04.5 12.84.58 17.21.1 5.71-.62 9.94-2.26 12.95-5.26 6.44-6.45 15.3-20.37 24.35-36.72zm0 450.21c-1.28-4.6-2.2-10.55-3.33-20.25l-.24-2.04-.23-2.03c-1.82-15.7-3.07-21.98-5.55-24.47-2.46-2.46-3.04-5.03-2.52-8.64.1-.6.18-1.1.39-2.15.69-3.54.77-5.04.08-6.84-.91-2.38-3.31-4.41-7.79-6.26-5.08-2.09-6.52-4.84-4.89-8.44.66-1.45 1.79-3.02 3.52-5.01 1.04-1.2 5.48-5.96 5.08-5.53 6.15-6.7 8.98-11.34 8.98-16.48a15.2 15.2 0 0 1 6.5-12.89v1.26a14.17 14.17 0 0 0-5.5 11.63c0 5.47-2.93 10.29-9.24 17.16.38-.42-4.04 4.33-5.07 5.5-1.67 1.93-2.75 3.43-3.36 4.77-1.37 3.04-.23 5.22 4.36 7.1 4.71 1.95 7.32 4.16 8.34 6.83.78 2.04.7 3.67-.03 7.4-.2 1.03-.3 1.51-.38 2.09-.48 3.33.03 5.59 2.23 7.8 2.74 2.74 3.98 8.96 5.84 25.06l.24 2.03.23 2.04c.82 7.01 1.53 12.06 2.34 16.03v4.33zm0-62.16c-1.4-3.13-4.43-9.9-4.95-11.17-1.02-2.53-1.25-3.8-.91-5.18.2-.84 2.05-4.68 2.32-5.33a70.79 70.79 0 0 0 3.54-11.2v3.99a62.82 62.82 0 0 1-2.62 7.6c-.31.75-2.09 4.46-2.27 5.18-.28 1.12-.08 2.22.87 4.57.41 1.02 2.5 5.7 4.02 9.09v2.45zm0-85.09c-1.65 1.66-3.66 2.9-6.4 4.13-.25.1-13.97 5.47-20.4 8.43-9.35 4.32-16.7 5.9-23.03 5.25-5.08-.53-9.02-2.25-14.77-5.92l-3.2-2.07a77.4 77.4 0 0 0-5.44-3.27c-4.05-2.18-3.25-5.8 1.47-10.47 3.71-3.68 9.6-7.93 18.73-13.8l4.46-2.82c17.95-11.33 18.22-11.5 22.27-14.74 11.25-9 19.69-14.02 26.31-15.1v1.02c-6.37 1.1-14.62 6-25.69 14.86-4.1 3.28-4.34 3.44-22.36 14.8a652.4 652.4 0 0 0-4.45 2.83c-9.07 5.83-14.92 10.05-18.57 13.66-4.31 4.28-4.95 7.13-1.7 8.88 1.7.91 3.29 1.88 5.5 3.3l3.2 2.08c5.64 3.59 9.45 5.25 14.34 5.76 6.13.64 13.32-.9 22.52-5.15 6.46-2.98 20.18-8.35 20.4-8.44 3.04-1.37 5.1-2.71 6.81-4.69v1.47zm0-41.37v1c-6.56.26-12.11 3.13-19.71 9.08l-4.63 3.68a51.87 51.87 0 0 1-4.4 3.14c-.82.52-5.51 3.33-6.22 3.76-3.31 2-6.15 3.8-8.87 5.6a112.61 112.61 0 0 0-8.16 5.92c-4.61 3.72-7.4 6.9-7.97 9.35-.63 2.67 1.48 4.53 7.05 5.46 10.7 1.78 20.92-.05 30.45-4.65a61.96 61.96 0 0 0 17.1-12.2 41.8 41.8 0 0 0 5.36-7.42v1.92a38.94 38.94 0 0 1-4.64 6.19 62.95 62.95 0 0 1-17.39 12.41c-9.7 4.68-20.13 6.55-31.05 4.73-6.06-1-8.65-3.29-7.85-6.67.64-2.74 3.53-6.05 8.31-9.9 2.35-1.9 5.1-3.88 8.24-5.97 2.73-1.82 5.58-3.61 8.9-5.62.72-.44 5.4-3.24 6.22-3.75 1.26-.8 2.6-1.76 4.3-3.09.8-.62 3.9-3.1 4.63-3.67 7.77-6.1 13.49-9.04 20.33-9.3zm0-154.6v1c-1.75-.24-4.3.23-7.82 1.55-10.01 3.75-13.8 5.07-19.15 6.76-1.78.56-2.63.83-3.87 1.24-1.48.5-3.16.76-6.74 1.16a1550.34 1550.34 0 0 0-2.64.3c-7.8.94-11.28 2.47-11.28 6.07 0 4.45 2.89 13.18 7.96 25.81a57.34 57.34 0 0 1 2.33 7.6 258.32 258.32 0 0 1 .84 3.46c1.86 7.62 3.17 10.71 5.56 11.67 2.21.88 4.7.6 7.47-.72 3.48-1.69 7.22-4.94 11.2-9.47 1.52-1.7 2.97-3.49 4.59-5.57l3.16-4.1c2.59-3.23 6.07-12.21 8.39-20.23v3.45c-2.29 7.2-5.27 14.5-7.61 17.41-.44.55-2.67 3.46-3.15 4.09-1.63 2.1-3.1 3.9-4.62 5.62-4.08 4.61-7.9 7.94-11.53 9.7-2.99 1.44-5.77 1.75-8.28.74-2.84-1.13-4.2-4.34-6.15-12.35a2097.48 2097.48 0 0 1-.84-3.46c-.8-3.2-1.47-5.45-2.28-7.46-5.14-12.8-8.04-21.55-8.04-26.19 0-4.37 3.84-6.06 12.16-7.07a160.9 160.9 0 0 1 2.65-.3c3.5-.39 5.15-.64 6.53-1.1 1.26-.42 2.1-.7 3.88-1.26 5.34-1.68 9.11-3 19.1-6.74 3.53-1.32 6.22-1.84 8.18-1.61zM0 292c10.13-11.31 18.13-23.2 23.07-35.39 3.3-8.14 6.09-16.12 10.81-30.55l1.59-4.84c6.53-19.94 10.11-29.82 14.77-39.56 6.07-12.72 12.55-21.18 20.27-25.54 6.66-3.76 10.2-7.86 12.22-13.15a46.6 46.6 0 0 0 1.86-6.58c1.23-5.2 2.05-7.59 3.93-10.36 2.45-3.62 6.27-6.53 12.1-8.96 15.78-6.58 16.73-7.04 18.05-9.01.65-.98.83-2.15.74-4.51-.03-.73-.23-3.82-.24-4A93.8 93.8 0 0 1 119 94c0-10.04.18-11.37 2.37-13.15.52-.42 1.13-.8 2.07-1.3.27-.14 2.18-1.12 2.84-1.48a68.4 68.4 0 0 0 9.12-5.87c2.06-1.54 2.64-2.14 8.01-7.93 3.78-4.09 6.21-6.36 8.96-8.12 3.64-2.33 7.2-3.12 10.9-2.11 4.4 1.2 10.81 2 18.78 2.46 6.9.4 12.9.5 21.95.5 4.87 0 8.97.47 15.4 1.57 7.77 1.33 9.3 1.54 12.38 1.54 4.05 0 7.43-.88 10.68-2.95 5.06-3.22 8.11-4.67 11.2-5.2 3.62-.64 4.77-.46 16.55 2.06 17.26 3.7 30.85 1.36 41.06-9.7 5.1-5.53 5.48-8.9 3.48-14.8-.83-2.42-1.03-3.1-1.17-4.3-.29-2.52.5-4.71 2.71-6.93 2.65-2.65 4.72-9.17 6.22-18.29h2.03c-1.56 9.71-3.77 16.65-6.83 19.7-1.79 1.8-2.36 3.39-2.14 5.28.11 1 .3 1.63 1.07 3.9 2.22 6.53 1.76 10.66-3.9 16.8-10.77 11.66-25.07 14.13-42.95 10.3-11.42-2.45-12.55-2.62-15.78-2.06-2.77.48-5.62 1.84-10.47 4.92a20.93 20.93 0 0 1-11.76 3.27c-3.25 0-4.81-.22-12.73-1.57C212.74 59.46 208.73 59 204 59c-9.1 0-15.11-.1-22.07-.5-8.09-.47-14.62-1.29-19.2-2.54-5.62-1.53-10.17 1.38-17.85 9.66-5.5 5.94-6.08 6.53-8.28 8.18a70.38 70.38 0 0 1-9.38 6.03c-.68.37-2.58 1.35-2.84 1.49-.84.44-1.35.76-1.75 1.08-1.47 1.2-1.63 2.4-1.63 11.6 0 1.85.06 3.54.17 5.44 0 .17.2 3.28.24 4.03.1 2.75-.13 4.29-1.08 5.71-1.67 2.5-2.27 2.8-18.95 9.74-5.48 2.29-8.99 4.96-11.2 8.24-1.71 2.51-2.47 4.73-3.64 9.7-.83 3.5-1.21 4.92-1.94 6.83-2.18 5.73-6.05 10.19-13.1 14.18-7.3 4.12-13.55 12.28-19.46 24.66-4.6 9.64-8.17 19.46-14.67 39.32l-1.58 4.84c-4.75 14.47-7.54 22.48-10.86 30.69-5.28 13.01-13.95 25.65-24.93 37.6v-2.97zm0 78v-.5l1-.01c6.32 0 7.47 5.2 4.6 13.36a60.36 60.36 0 0 1-5.6 11.3v-1.92a57.76 57.76 0 0 0 4.65-9.72c2.69-7.6 1.71-12.02-3.65-12.02-.34 0-.67 0-1 .02v-46.59a340.96 340.96 0 0 0 13.71-8.34c13.66-9.46 29.79-37.6 29.79-53.59 0-18.1 21.57-72.64 32.23-79.42 12.71-8.09 32.24-27.96 35.8-37.75 1.93-5.3 5.5-7.27 14.42-9.37 6.15-1.44 8.64-2.42 10.67-4.79 1.5-1.74 2.72-4.79 4.33-10.3.23-.78 1.9-6.68 2.43-8.46 3.62-12.08 7.3-18.49 13.47-20.39 2.5-.76 3.03-.98 9.74-3.7 7.49-3.03 11.97-4.43 17.12-4.92 6.75-.65 13.13.75 19.55 4.67 5.43 3.32 12.19 4.72 20.17 4.56 6.03-.12 12.2-1.07 19.83-2.8 1.82-.4 7.38-1.74 8.26-1.94 2.69-.6 4.34-.89 5.48-.89 4.97 0 8.93-.05 14.2-.27 7.9-.32 15.56-.92 22.75-1.88 8.5-1.14 15.9-2.73 21.88-4.82 18.9-6.62 32.64-18.3 33.67-27.59.29-2.56.4-2.96 2.79-11.11 2.33-7.95 3.21-12.93 2.72-18.23-.2-2.24-.69-4.38-1.48-6.42-1.5-3.92-2.63-9.4-3.43-16.18h.9c.77 6.47 1.89 11.72 3.47 15.82a24.93 24.93 0 0 1 1.54 6.69c.5 5.46-.4 10.54-2.77 18.6-2.36 8.06-2.47 8.47-2.74 10.95-1.09 9.75-15.1 21.68-34.33 28.41-6.06 2.12-13.52 3.72-22.09 4.87-7.22.96-14.92 1.57-22.83 1.89-5.3.21-9.27.27-14.25.27-1.04 0-2.64.27-5.26.87-.87.2-6.43 1.53-8.26 1.94-7.68 1.73-13.92 2.7-20.03 2.82-8.15.17-15.1-1.27-20.71-4.7-6.23-3.81-12.4-5.16-18.93-4.54-5.04.48-9.44 1.86-16.84 4.86-6.75 2.74-7.29 2.95-9.82 3.73-5.73 1.76-9.28 7.96-12.81 19.72-.53 1.77-2.2 7.66-2.43 8.46-1.66 5.65-2.91 8.78-4.53 10.67-2.22 2.58-4.84 3.62-12.01 5.3-7.8 1.83-11.13 3.66-12.9 8.54-3.65 10.04-23.32 30.06-36.2 38.25C65.94 190 44.5 244.2 44.5 262c0 16.34-16.3 44.78-30.22 54.41-2.14 1.48-8.24 5.12-14.28 8.68v-1.16 46.09zm0-173.7v-1.11c7.42-3.82 14.55-10.23 21.84-18.98 3.8-4.56 14.21-18.78 15.79-20.55 1.8-2.04 4.06-3.96 7.42-6.45 1.08-.8 4.92-3.57 5.49-3.99 9.36-6.85 14-11.96 15.98-19.36.8-2.98 1.54-6.78 2.46-12.3.23-1.44 2-12.46 2.56-15.79 2.87-16.77 5.73-26.79 10.07-32.1C92.46 52.43 101.5 38.13 101.5 33c0-2.54.34-3.35 6.05-15.71.68-1.49 1.25-2.74 1.77-3.93 2.5-5.75 3.9-10.04 4.14-13.36h1c-.23 3.48-1.66 7.87-4.23 13.76-.52 1.2-1.09 2.45-1.78 3.95-5.54 12.01-5.95 12.99-5.95 15.29 0 5.47-9.09 19.84-20.11 33.31-4.2 5.12-7.03 15.06-9.86 31.64-.57 3.33-2.33 14.33-2.57 15.78-.92 5.56-1.67 9.38-2.48 12.4-2.05 7.68-6.82 12.93-16.35 19.91l-5.49 3.98c-3.3 2.45-5.51 4.34-7.27 6.31-1.53 1.73-11.94 15.93-15.76 20.53-7.52 9.02-14.88 15.6-22.61 19.46zm0 361.83v-4.33c.48 2.36 1 4.35 1.6 6.15 2 6.03 4.6 8.26 8.19 6.59C28.76 557.69 43.5 542.4 43.5 527c0-16.2 6.37-31.99 17.1-46.3 1.88-2.5 3.66-4.4 5.53-6 .73-.62 1.45-1.18 2.3-1.8l2-1.43c3.68-2.68 5.32-5.28 7.08-12.59.75-3.07 1.38-5.02 4.2-13.26l.63-1.88c3.24-9.58 4.56-14.97 4.17-18.65-.48-4.43-3.8-5.23-11.3-1.64a81.12 81.12 0 0 1-9.15 3.7c-13.89 4.67-26.96 5.8-42.66 5.42l-1.95-.05-1.45-.02a39.8 39.8 0 0 0-15.05 2.96A21.81 21.81 0 0 0 0 438.37v-1.26a23.55 23.55 0 0 1 4.55-2.57 40.77 40.77 0 0 1 16.92-3.02l1.95.05c15.6.38 28.57-.75 42.32-5.37a80.12 80.12 0 0 0 9.04-3.65c8.04-3.84 12.16-2.85 12.72 2.43.42 3.89-.92 9.34-4.21 19.08l-.64 1.88c-2.8 8.2-3.43 10.15-4.16 13.18-1.82 7.52-3.59 10.34-7.47 13.16l-2 1.43c-.84.6-1.54 1.15-2.25 1.75a35.45 35.45 0 0 0-5.37 5.84c-10.61 14.15-16.9 29.74-16.9 45.7 0 15.88-15 31.45-34.29 40.45-4.3 2.01-7.39-.66-9.56-7.18-.23-.68-.44-1.39-.65-2.13zm0-62.16v-2.45l1.46 3.27c2.1 4.8 3.46 10.33 4.26 16.77.66 5.3.84 9.3 1.04 18.5.2 9.32.5 12.75 1.63 15.05 1.28 2.6 3.67 2.35 8.29-1.5 17.14-14.3 21.82-22.9 21.82-38.62 0-7.17 1.1-12.39 3.7-17.68 2.27-4.67 3.65-6.62 13.4-19.62a69.8 69.8 0 0 1 7.6-8.79 44.76 44.76 0 0 1 3.54-3.06c.38-.3.64-.52.89-.74a10.47 10.47 0 0 0 2.63-3.32 35.78 35.78 0 0 0 2.26-5.94l.37-1.2.36-1.15c.29-.91.48-1.55.66-2.16.45-1.53.74-2.68.91-3.66.38-2.2.12-3.49-.85-4.15-2.35-1.61-9.28-.24-23.8 4.94-9.54 3.4-16.12 4.17-27.85 4.26-7.71.06-10.43.4-13.25 2.12-3.48 2.12-5.84 6.4-7.58 14.26-.5 2.2-.99 4.19-1.49 5.98v-3.98l.51-2.22c1.8-8.1 4.28-12.6 8.04-14.9 3.04-1.85 5.86-2.2 13.77-2.26 11.61-.09 18.1-.84 27.51-4.2 14.93-5.32 21.95-6.71 24.7-4.83 1.38.94 1.71 2.6 1.28 5.15a33.69 33.69 0 0 1-.94 3.78l-.66 2.17-.36 1.15-.37 1.2a36.64 36.64 0 0 1-2.33 6.1c-.8 1.53-1.61 2.52-2.86 3.61l-.92.77-1.02.83c-.9.74-1.65 1.4-2.47 2.18a68.84 68.84 0 0 0-7.48 8.66c-9.7 12.93-11.07 14.87-13.31 19.46-2.52 5.15-3.59 10.22-3.59 17.24 0 16.04-4.82 24.91-22.18 39.38-5.04 4.2-8.18 4.55-9.83 1.18-1.22-2.5-1.52-5.94-1.73-15.47-.2-9.16-.38-13.15-1.03-18.4-.79-6.34-2.12-11.8-4.19-16.49L0 495.98zM379.27 0h1.04l1.5 5.26c3.28 11.56 4.89 19.33 5.26 27.8.49 11.01-1.52 21.26-6.63 31.17-7.8 15.13-20.47 26.5-36.22 34.1-12.38 5.96-26.12 9.17-36.22 9.17-6.84 0-17.24 1.38-37.27 4.62l-2.27.37c-24.5 3.99-31.65 5-37.46 5-3.49 0-4.08-.08-19.54-2.8-3.56-.64-6.32-1.1-9-1.5-20.23-2.96-31-1.2-31.96 7.86-.1.85-.18 1.72-.29 2.81l-.27 2.73c-1.1 10.9-2.02 15.73-4.31 19.96-2.9 5.34-7.77 7.95-15.63 7.95-10.2 0-12.92.6-15.5 3.17.52-.51-5.03 5.85-8.16 8.7-2.75 2.5-14.32 12.55-15.77 13.83a341.27 341.27 0 0 0-6.54 5.92c-6.97 6.49-11.81 11.76-14.6 16.15-5.92 9.3-10.48 18.04-11.69 24.08-1.66 8.3 3.67 9.54 19.02 1.21a626.23 626.23 0 0 1 44.54-21.9c3.5-1.56 14.04-6.2 15.68-6.95 5.05-2.25 8.3-3.8 10.78-5.15l1.95-1.07 2.18-1.18c1.76-.94 3.38-1.76 5-2.55 18.1-8.72 34.48-10.46 50.33-1.2 22.89 13.34 38.28 37.02 38.28 56.44 0 19.12-.73 25.13-5.18 33.2a45.32 45.32 0 0 1-4.94 7.12c-6.47 7.77-11.81 16.2-12.76 21.27-1.2 6.34 4.69 7.03 20.17-.05 13.31-6.08 22.4-14.95 28.5-26.32a80.51 80.51 0 0 0 6.1-15.13c.9-2.98 3.17-11.65 3.41-12.48a29.02 29.02 0 0 1 1.75-4.83c7.47-14.93 21.09-30.5 36.25-37.24 7.61-3.38 13-9.65 19.4-20.79.84-1.48 4.26-7.64 5.14-9.17 3.52-6.1 6.22-9.7 9.37-11.98 10.15-7.4 28.7-11.1 50.29-11.1 7.52 0 16.54-1.24 27.51-3.58a420.1 420.1 0 0 0 14.96-3.52c-1.3.33 15.54-3.98 19.42-4.89 14.15-3.33 41.07-5.01 64.11-5.01 17.36 0 27.82-9.23 38.53-38.67 6.62-18.21 6.62-26.37 2.69-34.35l-1.18-2.37A13.36 13.36 0 0 1 587.5 58c0-4.03 0-4.01 2.5-24.56.46-3.73.8-6.74 1.12-9.64.9-8.45 1.38-15.2 1.38-20.8 0-.94-.02-1.94-.04-3h1c.03 1.06.04 2.06.04 3 0 5.65-.48 12.43-1.39 20.9-.3 2.91-.66 5.93-1.11 9.66-2.5 20.45-2.5 20.47-2.5 24.44 0 1.97.45 3.57 1.45 5.68.24.51 1.16 2.35 1.17 2.36 4.06 8.24 4.06 16.68-2.65 35.13-10.84 29.8-21.63 39.33-39.47 39.33-22.96 0-49.83 1.68-63.89 4.99-3.86.9-20.69 5.2-19.4 4.88a421.05 421.05 0 0 1-14.99 3.53c-11.04 2.35-20.11 3.6-27.72 3.6-21.4 0-39.76 3.67-49.7 10.9-3 2.19-5.64 5.7-9.1 11.68-.87 1.52-4.29 7.68-5.14 9.17-6.49 11.3-12 17.71-19.86 21.2-14.9 6.63-28.38 22.03-35.75 36.77a28.17 28.17 0 0 0-1.69 4.67c-.23.8-2.5 9.49-3.4 12.5a81.48 81.48 0 0 1-6.19 15.3c-6.2 11.56-15.44 20.58-28.96 26.76-16.1 7.36-23 6.55-21.58-1.04 1-5.29 6.4-13.83 12.99-21.73a44.33 44.33 0 0 0 4.82-6.96c4.35-7.88 5.06-13.77 5.06-32.72 0-19.04-15.19-42.4-37.72-55.55-15.57-9.08-31.62-7.38-49.45 1.21a132.9 132.9 0 0 0-7.14 3.71l-1.95 1.07a158.83 158.83 0 0 1-10.85 5.19c-1.65.74-12.18 5.38-15.69 6.95a625.25 625.25 0 0 0-44.46 21.86c-15.95 8.66-22.37 7.16-20.48-2.29 1.24-6.2 5.83-15.02 11.82-24.42 2.85-4.48 7.74-9.8 14.77-16.34 1.98-1.85 4.12-3.79 6.56-5.94 1.46-1.29 13.02-11.33 15.75-13.82 3.09-2.8 8.6-9.14 8.14-8.67 2.82-2.82 5.75-3.46 16.2-3.46 7.5 0 12.04-2.43 14.75-7.42 2.2-4.07 3.11-8.84 4.2-19.59l.26-2.73.3-2.81c.56-5.42 4.47-8.5 11.23-9.6 5.44-.88 12.51-.51 21.86.86 2.7.4 5.47.86 9.04 1.49 15.33 2.7 15.96 2.8 19.36 2.8 5.73 0 12.9-1.03 37.3-5l2.27-.36c20.1-3.26 30.52-4.64 37.43-4.64 9.95 0 23.54-3.18 35.78-9.08 15.57-7.5 28.09-18.73 35.78-33.65 5.02-9.75 7-19.82 6.51-30.67-.37-8.37-1.96-16.08-5.23-27.57L379.27 0zm13.68 0h1.02c.78 3.9 1.92 8.7 3.51 14.88 3.63 14.05 3.06 27.03-.75 38.77a61 61 0 0 1-11.35 20.68 138.36 138.36 0 0 1-19.32 18.77c-11.32 9.02-23.36 15.49-35.95 18.39a258.63 258.63 0 0 1-22.57 4.07c-3.17.44-6.36.85-10.3 1.32l-9.39 1.12c-11.53 1.41-17.45 2.55-21.64 4.46-9.28 4.21-28.35 6.04-49.21 6.04-1.37 0-2.8-.12-4.3-.35-2.62-.41-5-1.03-9.14-2.29-7.34-2.21-9.63-2.75-12.63-2.56-3.9.23-6.63 2.29-8.47 6.89-1.86 4.66-2.42 7.53-3.34 14.98-1.1 8.98-2.87 12.12-9.97 14.3a40.12 40.12 0 0 0-6.8 2.66c-.63.33-1.16.64-1.76 1.02l-1.34.86c-1.9 1.14-3.86 1.49-9.25 1.49-3.2 0-8.83-.55-9.51-.39-1.22.28-.75-.14-7.14 6.24-1.5 1.5-3.49 3.18-6.32 5.37-1.52 1.18-7.16 5.43-7.94 6.03-4.96 3.78-8.33 6.6-11.06 9.38-4.88 4.98-6.85 9.15-5.56 12.7 1.34 3.67 4.07 4.42 8.9 2.82a55.72 55.72 0 0 0 7.77-3.48c1.5-.77 7.78-4.13 9.37-4.96a116.8 116.8 0 0 1 12.31-5.68 162.2 162.2 0 0 0 11.04-4.84c2.04-.97 10.74-5.16 13-6.22 4.41-2.1 8.1-3.78 11.65-5.29 17.14-7.3 29.32-9.9 37.67-6.65l5.43 2.1c2.3.88 4.17 1.62 6.02 2.38a150.9 150.9 0 0 1 13.07 6c18.34 9.63 30.35 22.13 34.79 39.87 6.96 27.85 3.6 45.53-8.08 62.4-3.97 5.75-3.52 9.2.06 8.97 4.14-.28 10.21-4.95 15.11-12.52 3.1-4.8 5.1-10.45 8.05-21.53l1.69-6.35c.66-2.47 1.24-4.52 1.83-6.5 4.93-16.56 11-27.28 21.56-34.76 7.15-5.06 23.73-15.5 25.48-16.75 6.74-4.81 10.53-9.44 14.34-18 7.74-17.44 21.09-24.34 44.47-24.34 9.36 0 17.91-1.13 29.53-3.49a624.86 624.86 0 0 0 6.2-1.28c2.4-.5 4.07-.84 5.66-1.13 4.03-.74 7.04-1.1 9.61-1.1 4.44 0 9.39-1 31.39-5.99l2.95-.66c16.34-3.67 25.64-5.35 31.66-5.35 1.54 0 2.4.01 6.4.1 7.8.15 12.27.13 17.33-.2 16.41-1.06 26.73-5.36 29.8-14.56a87.1 87.1 0 0 1 3.55-8.83c-.15.31 2.29-4.96 2.9-6.38 5.38-12.3 5.57-21.92-1.44-39.44a86.4 86.4 0 0 1-5.26-20.72c-1.61-11.98-1.38-23.14.1-40.35l.2-2.12h1l-.2 2.2c-1.48 17.15-1.7 28.24-.11 40.14a85.4 85.4 0 0 0 5.2 20.47c7.1 17.78 6.91 27.67 1.43 40.22-.62 1.43-3.06 6.72-2.91 6.4a86.17 86.17 0 0 0-3.52 8.73c-3.23 9.72-13.9 14.15-30.68 15.24-5.1.33-9.58.35-17.42.2-3.98-.09-4.84-.1-6.37-.1-5.91 0-15.18 1.67-31.44 5.32l-2.95.67c-22.16 5.02-27.05 6.01-31.61 6.01-2.5 0-5.45.36-9.43 1.09-1.58.29-3.25.62-5.64 1.11a4894.21 4894.21 0 0 0-6.2 1.29c-11.68 2.37-20.3 3.51-29.73 3.51-23.02 0-36 6.71-43.53 23.66-3.9 8.8-7.82 13.58-14.7 18.5-1.78 1.27-18.36 11.7-25.48 16.75-10.34 7.32-16.3 17.87-21.19 34.23-.58 1.96-1.15 4-1.82 6.47l-1.69 6.35c-2.98 11.18-5 16.9-8.17 21.81-5.05 7.81-11.37 12.68-15.89 12.98-4.7.31-5.3-4.23-.94-10.53 11.52-16.64 14.82-34.03 7.92-61.6-4.35-17.42-16.16-29.72-34.27-39.22-4-2.1-8.2-4-12.99-5.97-1.84-.75-3.7-1.49-6-2.38l-5.43-2.08c-8.03-3.12-20.02-.58-36.92 6.63-3.52 1.5-7.21 3.19-11.61 5.27l-13 6.22c-4.71 2.22-8.16 3.75-11.11 4.88a115.87 115.87 0 0 0-12.21 5.63c-1.58.83-7.86 4.18-9.37 4.96a56.55 56.55 0 0 1-7.9 3.54c-5.3 1.75-8.62.85-10.17-3.43-1.46-4.02.66-8.5 5.8-13.74 2.75-2.82 6.16-5.66 11.15-9.48.79-.6 6.43-4.85 7.94-6.02a66.96 66.96 0 0 0 6.23-5.28c6.74-6.74 6.1-6.16 7.61-6.51.87-.2 6.69.36 9.74.36 5.22 0 7.03-.32 8.74-1.35l1.31-.84c.62-.4 1.18-.72 1.84-1.07a41.07 41.07 0 0 1 6.96-2.72c6.64-2.04 8.22-4.84 9.28-13.47.93-7.53 1.5-10.47 3.4-15.24 1.99-4.95 5.04-7.26 9.34-7.51 3.17-.2 5.5.35 12.97 2.6a63.54 63.54 0 0 0 9.02 2.26c1.45.22 2.83.34 4.14.34 20.71 0 39.7-1.82 48.8-5.96 4.32-1.96 10.29-3.1 21.93-4.53l9.4-1.12c3.92-.48 7.11-.88 10.27-1.32 8.16-1.14 15.4-2.43 22.49-4.06 12.42-2.86 24.33-9.26 35.55-18.2a137.4 137.4 0 0 0 19.18-18.64 60.02 60.02 0 0 0 11.15-20.32c3.76-11.57 4.32-24.36.75-38.23A284.86 284.86 0 0 1 392.95 0zM506.7 0h1.26c-.5.66-.9 1.18-1.17 1.51-3.95 4.96-6.9 7.92-9.82 9.57A10.02 10.02 0 0 1 492 12.5c-2.38 0-4.24.67-6.71 2.21l-2.65 1.71c-4.38 2.8-8.01 4.08-13.64 4.08-5.6 0-9.99-1.26-16.08-4.05a202.63 202.63 0 0 1-2.3-1.06l-2.18-.98c-1.6-.7-2.92-1.17-4.17-1.48a13.42 13.42 0 0 0-3.27-.43c-2.3 0-4.3-.68-11-3.37l-1.56-.62c-5-1.97-8.1-2.82-10.52-2.66-2.93.2-4.42 2.03-4.42 6.15 0 20.76-5.21 50.42-12.15 57.35-7.58 7.59-26.55 23.7-34.06 29.06-13.16 9.4-31.17 20.2-44.11 25.06a106.87 106.87 0 0 1-13.32 4.03c-3.28.78-6.6 1.43-11.25 2.24-.53.1-8.8 1.5-11.5 1.99-4.86.87-9.3 1.74-14 2.76-20.62 4.48-25.07 5.01-38.11 5.01-2.49 0-2.9-.07-14.05-2-2.42-.42-4.31-.73-6.15-1-8.11-1.19-13.83-1.36-17.64-.2-4.54 1.4-5.93 4.65-3.7 10.52 2.02 5.28 4.84 8.61 8.84 10.74 3.26 1.74 6.75 2.6 13.82 3.71 9.42 1.48 10.94 1.75 15.5 2.92a78.2 78.2 0 0 1 18.62 7.37c8.3 4.58 14.58 11.5 19.98 20.89 2.73 4.73 9.46 19.33 10.54 21.19 3.4 5.85 6.26 6.63 10.89 2 4.95-4.94 10.35-8.37 21.13-14.06.47-.25 2.06-1.1 2.12-1.12 7.98-4.21 11.92-6.51 15.87-9.54 5.11-3.9 8.66-8.1 10.77-13.11 8.52-20.24 20.75-33.31 32.46-33.31l5.5.03c10.53.08 17.35.02 24.9-.31 13.66-.62 23.78-2.09 29.39-4.67 5.85-2.7 13.42-5.49 24.18-9.02 3.46-1.14 6.29-2.05 12.7-4.1 7.7-2.45 11.08-3.54 15.17-4.9a1059.43 1059.43 0 0 1 11.33-3.72c3.67-1.2 5.96-2 8.03-2.78a59.88 59.88 0 0 0 6.66-2.94c1.87-.98 3.76-2.1 5.86-3.5 3.48-2.33 6.15-3.13 12.04-4.13l1.15-.2c5.71-1.01 9-2.3 12.76-5.63 7.82-6.96 8.58-23.18 3.84-44.52-1.7-7.67-2.1-19.28-1.57-35.47A837.22 837.22 0 0 1 546.76 0h1l-.15 3.06c-.32 6.42-.53 11.02-.68 15.62-.51 16.1-.12 27.65 1.56 35.21 4.82 21.68 4.04 38.2-4.16 45.48-3.91 3.48-7.37 4.84-13.24 5.87l-1.16.2c-5.76.99-8.32 1.75-11.65 3.98a63.73 63.73 0 0 1-5.96 3.56 60.86 60.86 0 0 1-6.77 2.99c-2.09.79-4.39 1.58-8.07 2.79a5398.31 5398.31 0 0 1-11.32 3.71c-4.1 1.37-7.48 2.46-15.18 4.92-6.42 2.04-9.24 2.95-12.7 4.08-10.73 3.53-18.27 6.3-24.07 8.98-5.76 2.66-15.97 4.14-29.77 4.77-7.56.33-14.4.39-24.95.31l-5.49-.03c-11.19 0-23.16 12.79-31.54 32.7-2.19 5.19-5.84 9.52-11.08 13.52-4.02 3.07-7.99 5.39-16.01 9.62l-2.12 1.12c-10.7 5.65-16.04 9.04-20.9 13.9-5.14 5.14-8.75 4.15-12.45-2.22-1.12-1.92-7.85-16.5-10.54-21.2-5.33-9.24-11.48-16.02-19.6-20.5a77.2 77.2 0 0 0-18.4-7.28c-4.5-1.17-6.02-1.43-15.4-2.9-7.17-1.12-10.74-2-14.13-3.81-4.22-2.25-7.2-5.77-9.3-11.27-2.43-6.39-.78-10.26 4.34-11.83 4-1.22 9.82-1.05 18.08.17 1.84.27 3.74.58 6.17 1 11.02 1.9 11.48 1.98 13.88 1.98 12.96 0 17.35-.52 37.9-4.99 4.71-1.02 9.16-1.9 14.03-2.77 2.71-.48 10.98-1.9 11.5-1.98 4.64-.81 7.95-1.46 11.2-2.23 4.55-1.07 8.76-2.34 13.2-4 12.83-4.81 30.79-15.59 43.88-24.94 7.47-5.33 26.4-21.4 33.94-28.94C407.3 61.98 412.5 32.49 412.5 12c0-4.61 1.86-6.9 5.35-7.15 2.63-.18 5.8.7 10.96 2.73l1.56.62c6.53 2.62 8.53 3.3 10.63 3.3 1.14 0 2.3.16 3.5.46 1.32.33 2.68.82 4.34 1.53a90.97 90.97 0 0 1 3.34 1.52l1.15.54c5.98 2.73 10.23 3.95 15.67 3.95 5.41 0 8.87-1.21 13.1-3.92.2-.13 2.1-1.38 2.66-1.72 2.62-1.63 4.64-2.36 7.24-2.36 1.47 0 2.94-.43 4.47-1.3 2.78-1.56 5.67-4.45 9.54-9.31l.7-.89zM324.54 600h-2.03c.49-2.96.91-6.2 1.28-9.66.44-4.1.76-8.25.98-12.21.08-1.39.14-2.65-.35-7.29-.47-1.94-.93-4.14-1.36-6.54-2.01-11.26-2.66-22.9-1.14-33.78a60.76 60.76 0 0 1 5.18-17.95 70.78 70.78 0 0 1 12.6-18.22c3.38-3.6 5.53-5.5 11.83-10.79 4.5-3.78 6.35-5.56 7.52-7.5.64-1.07.95-2.06.95-3.06 0-1.75 0-1.74-.75-9.23-.36-3.7-.57-6.3-.68-8.96-.5-12.1 1.62-19.6 8.11-21.76 15.9-5.3 25.89-12.1 33.45-25.54C409.6 390.65 425.85 376 436 376c12.36 0 20-1.96 29.41-8.8 6.76-4.92 9.5-6.6 12.47-7.46 2.22-.64 3.8-.74 9.12-.74 1.86 0 3.53-.83 5.57-2.62 1.08-.96 5.11-5.12 5.6-5.6 6.04-5.85 11.98-8.78 20.83-8.78 2.45 0 4.54.04 7.32.12 7.51.23 8.87.17 11.27-.7 3.03-1.1 5.53-3.03 14.75-11.17 8-7.06 10.72-8.92 22.87-16.47 1.44-.9 2.59-1.63 3.69-2.37a69.45 69.45 0 0 0 9.46-7.5c4.12-3.88 8.02-7.85 11.64-11.9v2.98a201.58 201.58 0 0 1-10.27 10.38c-3.18 3-6.2 5.35-9.72 7.7-1.12.76-2.28 1.5-3.75 2.4-12.05 7.5-14.71 9.32-22.6 16.28-9.46 8.35-12.01 10.32-15.39 11.55-2.74 1-4.19 1.06-12.01.82-2.76-.08-4.83-.12-7.26-.12-8.27 0-13.75 2.7-19.43 8.22-.44.43-4.52 4.64-5.68 5.66-2.37 2.09-4.46 3.12-6.89 3.12-5.1 0-6.6.1-8.56.66-2.67.78-5.29 2.37-11.85 7.15-9.8 7.13-17.85 9.19-30.59 9.19-9.22 0-24.96 14.2-34.13 30.49-7.84 13.94-18.24 21.02-34.55 26.46-5.31 1.77-7.21 8.51-6.75 19.78.1 2.6.31 5.19.68 8.84.75 7.62.75 7.58.75 9.43 0 1.38-.42 2.73-1.24 4.09-1.33 2.2-3.26 4.07-7.94 8-6.25 5.24-8.36 7.12-11.67 10.63a68.8 68.8 0 0 0-12.25 17.71 58.8 58.8 0 0 0-5 17.36c-1.49 10.66-.85 22.09 1.13 33.15.43 2.37.88 4.53 1.33 6.44.16.66.3 1.25.6 4.06a249.3 249.3 0 0 1-1.17 16.12c-.37 3.37-.78 6.53-1.25 9.44zm-13.4 0h-1.05l.12-.28c3.07-7.16 4.29-11.83 4.29-18.72 0-3.57-.07-4.93-.76-15.65-.77-12.04-1-19.64-.55-28.3.58-11.5 2.4-22.1 5.81-32.16 1.3-3.8 2.8-7.5 4.55-11.1 3.46-7.14 6.83-12.39 10.42-16.6a59.02 59.02 0 0 1 4.35-4.56c.43-.4 3-2.8 3.67-3.45 5.72-5.6 7.51-11.52 7.51-29.18 0-18.84 2.9-23.77 15.82-28.24 1.09-.37 1.92-.67 2.77-.98a51.3 51.3 0 0 0 6.1-2.7c4.95-2.6 9.64-6.22 14.44-11.42 25.5-27.63 37.15-35.16 56.37-35.16 8.28 0 14.54-1.95 22-6.3 1.78-1.03 13.82-8.82 18.16-11.27 2.83-1.59 5.66-3.03 8.63-4.39 7.92-3.6 13.97-4.45 26.6-4.8 7.53-.2 10.7-.49 14.26-1.58 4.55-1.4 8.06-4 10.93-8.43 2.2-3.41 6.85-7.08 14.66-12.06 1.61-1.03 3.27-2.05 5.65-3.5 9.53-5.85 11.56-7.13 14.81-9.57 5.34-4 9.3-8.37 13.68-14.77a204.2 204.2 0 0 0 5.62-8.75v1.9c-1.97 3.17-3.4 5.38-4.8 7.42-4.42 6.48-8.46 10.92-13.9 15-3.29 2.46-5.32 3.75-14.89 9.61a375.06 375.06 0 0 0-5.63 3.5c-7.7 4.9-12.26 8.52-14.36 11.76-3 4.63-6.7 7.39-11.48 8.85-3.68 1.12-6.9 1.42-14.53 1.63-12.5.34-18.44 1.18-26.2 4.7a111.08 111.08 0 0 0-8.56 4.35c-4.3 2.43-16.34 10.22-18.15 11.27-7.6 4.43-14.03 6.43-22.5 6.43-18.87 0-30.3 7.4-55.63 34.84-4.88 5.28-9.67 8.97-14.7 11.62-2 1.05-4 1.92-6.23 2.75-.86.32-1.7.62-5.37 1.87-5.08 1.76-7.44 3.25-9.28 6.37-2.23 3.78-3.29 9.94-3.29 20.05 0 17.9-1.87 24.07-7.8 29.89-.69.67-3.27 3.06-3.69 3.46a58.04 58.04 0 0 0-4.28 4.49c-3.53 4.14-6.86 9.32-10.28 16.38a95.19 95.19 0 0 0-4.5 10.99c-3.38 9.97-5.18 20.48-5.76 31.9-.44 8.6-.22 16.17.55 28.17.69 10.76.76 12.12.76 15.72 0 6.35-1.02 10.87-4.35 19zm25.08 0h-1c-.04-4.73.06-9.39.28-15.02.26-6.41-.4-11.79-2.53-24.37l-.31-1.86c-2.12-12.55-2.76-19.35-1.97-26.47 1.03-9.25 4.75-16.68 12-22.67 22.04-18.2 29.81-30.18 29.81-44.61 0-2.6-.3-4.81-.98-8.17-.97-4.79-1.1-5.68-.97-7.57.2-2.56 1.27-4.7 3.56-6.72 2.67-2.35 7.05-4.6 13.72-7.01 9.72-3.5 15.52-9.18 24.3-21.57l1.78-2.5c4.48-6.33 7.1-9.63 10.43-12.78 4.31-4.07 8.98-6.77 14.54-8.17 13.3-3.32 20.37-5.47 25.34-7.64a49.5 49.5 0 0 0 5.28-2.7c1.1-.65 1.75-1.04 4.24-2.6 2.7-1.68 5.22-2.08 11.38-2.28 5.44-.18 7.9-.43 10.97-1.41a21.47 21.47 0 0 0 9.54-6.22c4.87-5.3 10.03-7.61 17.79-8.9 1.07-.18 1.88-.3 3.86-.58 6.9-.97 9.94-1.69 13.48-3.62 4.5-2.45 6.79-4.44 23.46-19.68l3.14-2.85c9.65-8.71 16.12-13.83 21.42-16.48 4.25-2.12 7.6-4.69 11.22-8.6v1.45c-3.42 3.57-6.69 6-10.78 8.05-5.18 2.59-11.61 7.67-21.2 16.32l-3.12 2.85c-16.8 15.35-19.05 17.3-23.66 19.82-3.68 2-6.8 2.75-13.82 3.73-1.97.28-2.78.4-3.84.57-7.56 1.26-12.52 3.48-17.21 8.6a22.47 22.47 0 0 1-9.97 6.5c-3.2 1-5.72 1.27-11.25 1.45-5.98.2-8.39.57-10.89 2.13a144 144 0 0 1-4.25 2.61 50.48 50.48 0 0 1-5.39 2.75c-5.04 2.2-12.15 4.37-25.5 7.7-9.74 2.44-15.26 7.65-24.4 20.56l-1.77 2.5c-8.9 12.54-14.82 18.34-24.78 21.93-6.57 2.36-10.85 4.57-13.4 6.82-2.1 1.86-3.05 3.74-3.22 6.04-.13 1.76 0 2.63.95 7.3.7 3.42 1 5.7 1 8.37 0 14.79-7.93 27-30.18 45.39-7.03 5.8-10.64 13-11.64 22-.78 7-.14 13.73 1.96 26.2l.32 1.85c2.15 12.65 2.8 18.07 2.54 24.58-.22 5.57-.32 10.2-.28 14.98zM95.9 600h-2.04c.68-3.82 1.14-8.8 1.61-15.98.2-3.11.27-4.06.39-5.6 1.3-17.54 4.04-27.14 11.5-33.2 4.65-3.77 7.22-8.92 8.67-16 .51-2.52.7-3.87 1.33-9.17.66-5.5 1.16-8.06 2.24-10.36 1.45-3.09 3.82-4.69 7.39-4.69 14.28 0 38.48 9.12 53.6 20.2 8.66 6.35 21.26 13.32 31.74 17.11 13.03 4.71 21.89 4.41 24.75-1.73 1.7-3.64 1.92-4.11 2.65-5.77 2.93-6.67 4.69-12.2 5.25-17.5.23-2.17.24-4.23.02-6.2-.32-2.75-1.42-4.55-4.08-7.35l-1.32-1.37a30.59 30.59 0 0 1-2.41-2.79 30.37 30.37 0 0 1-2.5-4.07l-1.13-2.14c-1.62-3.1-2.68-4.6-4.12-5.56-5.26-3.5-14.8-5.5-28.55-6.83a272.42 272.42 0 0 0-9.04-.71l-2.18-.17c-9.57-.73-15.12-1.56-19.06-3.2C156.57 471.07 136 450.5 136 440c0-5.34 1.74-9.53 5.47-14.13 1.98-2.44 11.12-11.71 12.79-13.54 4.52-4.97 10.16-9.54 17.68-14.66 2.8-1.9 14.78-9.6 17.49-11.49a50.54 50.54 0 0 0 6.34-5.43c1.53-1.5 6.96-7.13 7.12-7.3 7.18-7.3 12.7-11.56 19.74-14.38 3.36-1.34 8.13-2.79 17.45-5.38a9577.18 9577.18 0 0 1 11.78-3.28 602.6 602.6 0 0 0 12.67-3.7c20.4-6.24 34-12.08 40.79-18.44 8.74-8.2 11.78-13.84 15.73-26.02 2.02-6.22 3.09-9.04 5.07-12.72 9.54-17.71 28.71-39.37 43.5-45.45C383.77 238.25 389 232.34 389 226c0-2.89 2.73-8.4 6.83-13.73 4.76-6.2 10.65-11.36 16.75-14.18 12.5-5.77 33.5-10.09 47.42-10.09 5.32 0 9.83-1.5 16.42-4.89 9.2-4.71 10.1-5.11 13.58-5.11 10.42 0 32.06-2.55 45.76-5.97l3.88-.98 3.47-.89c2.6-.66 4.33-1.08 5.93-1.43 3.9-.86 6.76-1.23 9.58-1.17 2.74.06 5.47.52 8.67 1.48 4.56 1.37 13.71-.9 22.87-5.68a68.07 68.07 0 0 0 9.84-6.2v2.4c-11.09 8.14-25.76 13.66-33.29 11.4a29.72 29.72 0 0 0-8.13-1.4c-2.63-.05-5.36.3-9.11 1.12a238 238 0 0 0-9.33 2.3l-3.9.99C522.38 177.43 500.58 180 490 180c-2.99 0-3.91.4-12.67 4.89-6.85 3.51-11.61 5.11-17.33 5.11-13.65 0-34.35 4.26-46.58 9.9-5.78 2.67-11.42 7.62-16 13.58-3.85 5.02-6.42 10.2-6.42 12.52 0 7.27-5.8 13.82-20.62 19.92-14.27 5.88-33.16 27.21-42.5 44.55-1.9 3.55-2.95 6.28-4.93 12.4-4.05 12.47-7.23 18.39-16.27 26.86-7.08 6.64-20.87 12.57-41.57 18.89a604.52 604.52 0 0 1-12.7 3.71 1495.1 1495.1 0 0 1-11.8 3.28c-9.24 2.58-13.97 4.01-17.24 5.32-6.73 2.69-12.05 6.8-19.05 13.92-.15.15-5.6 5.8-7.15 7.32a52.4 52.4 0 0 1-6.6 5.65c-2.74 1.92-14.75 9.63-17.5 11.5-7.4 5.04-12.94 9.52-17.33 14.35-1.72 1.9-10.8 11.11-12.71 13.46-3.47 4.26-5.03 8.03-5.03 12.87 0 9.5 20 29.5 33.38 35.08 3.67 1.53 9.1 2.34 18.45 3.05a586.23 586.23 0 0 0 4.34.32c3.24.23 5.07.37 6.93.55 14.08 1.37 23.82 3.4 29.45 7.17 1.82 1.2 3.02 2.91 4.8 6.29l1.11 2.13a28.55 28.55 0 0 0 2.34 3.81c.62.83 1.3 1.6 2.26 2.61.23.24 1.1 1.16 1.32 1.37 2.93 3.09 4.24 5.23 4.61 8.5.24 2.12.23 4.33-.01 6.64-.59 5.55-2.4 11.25-5.41 18.1-.74 1.67-.96 2.15-2.66 5.8-3.49 7.47-13.33 7.8-27.25 2.77-10.67-3.86-23.43-10.92-32.25-17.38C164.62 515.96 140.82 507 127 507c-5 0-6.4 3.02-7.64 13.29a99.03 99.03 0 0 1-1.36 9.33c-1.53 7.5-4.3 13.04-9.37 17.16-6.87 5.58-9.5 14.78-10.77 31.8-.11 1.52-.18 2.47-.38 5.57-.46 7.01-.91 11.99-1.57 15.85zm8.05 0h-1.02c.29-1.41.58-2.94.9-4.59l1.05-5.62c2.5-13.3 4.2-19.92 6.68-24.05 1.7-2.84 3.68-5.5 8.05-11.03 8.21-10.36 10.88-14.55 10.88-18.71l-.02-1.69c-.02-1.78-.02-2.7.02-3.77.21-5.05 1.47-8.2 4.64-9.4 3.92-1.5 10.39.44 20.12 6.43 9.56 5.88 17.53 10.7 25.91 15.66 1.31.78 14.27 8.41 17.67 10.45a714.21 714.21 0 0 1 6.42 3.9c13.82 8.5 38.94 5.05 46.3-7.83 3.6-6.28 4.54-8.52 7.78-17.32a82.3 82.3 0 0 1 1.18-3.07 42.27 42.27 0 0 1 4.06-7.64c9.33-13.98 14.92-26.1 14.92-36.72 0-3.66.75-6.62 3.36-14.85.52-1.64.83-2.66 1.15-3.73 3.64-12.23 3.04-19.12-4.29-24a23.1 23.1 0 0 0-9.98-3.78c-7.2-.93-14.49 1.17-23.91 5.88-1.55.78-6.64 3.44-7.6 3.93a62.6 62.6 0 0 0-4.14 2.3l-4.4 2.66c-11.62 6.92-20.4 9.18-32.81 6.08-3.32-.84-6.24-1.4-13.1-2.64-13.25-2.39-18.7-3.75-23.33-6.46-6.23-3.67-7.46-9.02-2.88-16.65A93.1 93.1 0 0 1 172 415.42a157 157 0 0 1 8.32-7.66c-.07.05 6.16-5.3 7.82-6.77a85.12 85.12 0 0 0 6.5-6.33c7.7-8.46 12.78-13.36 20.08-18.57 9.94-7.1 21.4-12.36 35.18-15.58 37.03-8.64 51-12.7 58.83-17.93 8.6-5.73 21.3-24.77 36.84-54.81 5.22-10.1 12.27-18.4 21.13-25.71 5.13-4.24 9.56-7.25 17.55-12.23 7.42-4.62 9.62-6.14 11.38-8.16a21.15 21.15 0 0 0 2.95-4.87c.61-1.3 2.87-6.47 3-6.77 1.36-3 2.56-5.4 3.95-7.73 6.53-10.97 16.03-18 31.4-20.8 12.73-2.3 19.85-2.7 29.68-2.3 3.25.13 4.13.16 5.6.14 5.15-.07 9.71-1.04 16.61-3.8 20.74-8.3 38.75-12.04 59.19-12.04 3.05 0 6.03.15 10.48.48l2.09.16c12.45.96 18.08.96 25.34-.63a49.65 49.65 0 0 0 14.09-5.45v1.15a50.52 50.52 0 0 1-13.88 5.28c-7.38 1.61-13.08 1.61-25.63.65l-2.08-.16c-4.43-.33-7.39-.48-10.41-.48-20.3 0-38.2 3.72-58.81 11.96-7.01 2.8-11.7 3.8-16.97 3.88-1.5.02-2.39-.01-5.66-.14-9.76-.4-16.8-.01-29.47 2.3-15.06 2.73-24.32 9.58-30.71 20.31a72.8 72.8 0 0 0-3.9 7.63c-.12.28-2.39 5.47-3.01 6.79a22 22 0 0 1-3.1 5.1c-1.86 2.13-4.07 3.66-11.6 8.35-7.95 4.96-12.35 7.95-17.44 12.15-8.76 7.23-15.73 15.43-20.89 25.4-15.61 30.2-28.36 49.32-37.16 55.19-7.98 5.32-21.97 9.39-59.17 18.07-13.65 3.18-24.98 8.39-34.82 15.42-7.22 5.16-12.27 10.01-19.92 18.43a86.07 86.07 0 0 1-6.57 6.4c-1.67 1.48-7.91 6.83-7.84 6.77-3.27 2.84-5.8 5.16-8.26 7.62a92.1 92.1 0 0 0-14.27 18.13c-4.3 7.16-3.22 11.89 2.53 15.26 4.47 2.63 9.88 3.99 23.24 6.39a185.7 185.7 0 0 1 12.92 2.6c12.11 3.03 20.64.84 32.06-5.96l4.4-2.65c1.66-1 2.96-1.73 4.2-2.35.95-.48 6.04-3.14 7.6-3.92 9.59-4.8 17.04-6.94 24.49-5.98a24.1 24.1 0 0 1 10.4 3.93c7.82 5.21 8.45 12.52 4.7 25.13-.32 1.07-.64 2.1-1.16 3.74-2.57 8.12-3.31 11.04-3.31 14.55 0 10.88-5.66 23.14-15.08 37.28a41.28 41.28 0 0 0-3.97 7.46c-.37.9-.73 1.82-1.18 3.04-3.25 8.85-4.21 11.13-7.84 17.47-7.67 13.42-33.43 16.95-47.7 8.18a578.4 578.4 0 0 0-6.4-3.89c-3.4-2.04-16.36-9.67-17.67-10.45-8.38-4.97-16.36-9.78-25.92-15.66-9.5-5.85-15.7-7.7-19.24-6.36-2.68 1.02-3.8 3.82-4 8.51a61.12 61.12 0 0 0-.02 3.72l.02 1.7c0 4.5-2.69 8.73-11.52 19.87-3.92 4.95-5.87 7.59-7.55 10.39-2.39 3.97-4.08 10.56-6.56 23.72l-1.05 5.62-.86 4.4zm10.5 0h-1c.03-.34.04-.68.04-1 0-12.39 8.48-33.57 19.16-43.37a26.18 26.18 0 0 0 3.67-4.17 35.8 35.8 0 0 0 2.88-4.9c.36-.72 1.75-3.66 2.1-4.36 3.22-6.29 6.84-6.54 16.97.39 1.34.9 6.07 4.16 6.4 4.38 2.62 1.8 4.67 3.2 6.7 4.56 5.03 3.39 9.37 6.2 13.51 8.7 14.33 8.67 25.49 13.27 34.11 13.27 16.86 0 32.71-5.95 39.6-14.8 1.59-2.04 3.2-5.17 5.06-9.63.8-1.92 1.64-4.06 2.67-6.8l2.74-7.33c4.66-12.44 7.76-19.06 11.56-23.27 7.9-8.79 14.87-36 14.87-52.67 0-1.9.17-3.11 1.02-8.27.37-2.2.58-3.6.74-5.07.63-5.51.21-9.46-1.68-12.39-4.6-7.1-19.7-9.23-38.46-4.78a100.57 100.57 0 0 0-18.94 6.3c-5.17 2.37-17.11 9.74-16.5 9.4-6.72 3.64-12.97 4.15-24.8 1.3-29.55-7.14-30.43-8.62-15.26-26.81 17.44-20.93 47.12-46.18 56.38-46.18 9.92 0 53.84-11.98 65.78-17.95 9.46-4.73 24.32-21.18 36.82-37.85.71-.95 13.5-21.6 19.2-29.6 9.35-13.13 18.22-22.55 26.95-27.53 7.29-4.17 13.16-10.28 18.8-18.73 1.93-2.9 10.52-17.65 12.73-20.41 1.54-1.93 3-3.21 4.52-3.89 14.07-6.25 24.22-9.04 39.2-9.04h29c4.05 0 7.36-.4 22.93-2.5l4.3-.57c9.92-1.3 16.57-1.93 21.77-1.93 1.66 0 2.95.01 6.03.04 18.61.19 28.55-.48 44.86-4.03 3.1-.67 6.13-1.78 9.11-3.31v1.12a37.96 37.96 0 0 1-8.9 3.17c-16.4 3.56-26.4 4.24-45.08 4.05-3.08-.03-4.36-.04-6.02-.04-5.15 0-11.76.63-21.64 1.92l-4.3.58c-15.64 2.11-18.94 2.5-23.06 2.5h-29c-14.81 0-24.84 2.75-38.8 8.96-1.34.6-2.69 1.78-4.14 3.6-2.16 2.68-10.72 17.39-12.68 20.33-5.72 8.57-11.7 14.8-19.13 19.04-8.57 4.9-17.36 14.23-26.63 27.24-5.68 7.97-18.47 28.64-19.22 29.63-12.6 16.8-27.52 33.32-37.18 38.15-12.06 6.03-56.14 18.05-66.22 18.05-8.82 0-38.39 25.15-55.62 45.82-14.6 17.52-14.19 18.21 14.74 25.2 11.6 2.8 17.6 2.3 24.09-1.2-.67.35 11.31-7.03 16.56-9.44 5.41-2.48 11.6-4.59 19.11-6.37 19.13-4.53 34.65-2.35 39.54 5.22 2.05 3.17 2.48 7.32 1.84 13.04a96.34 96.34 0 0 1-.75 5.13c-.84 5.08-1.01 6.29-1.01 8.1 0 16.9-7.03 44.33-15.13 53.33-3.68 4.09-6.76 10.65-11.37 22.96-.35.93-2.2 5.94-2.73 7.33-1.04 2.76-1.88 4.9-2.68 6.84-1.9 4.53-3.55 7.73-5.2 9.85-7.1 9.13-23.25 15.19-40.39 15.19-8.86 0-20.15-4.65-34.63-13.42-4.15-2.51-8.5-5.32-13.55-8.72a861.54 861.54 0 0 1-6.71-4.56l-6.4-4.39c-9.68-6.63-12.61-6.42-15.5-.75-.35.68-1.74 3.62-2.1 4.35a36.77 36.77 0 0 1-2.96 5.03c-1.12 1.57-2.37 3-3.81 4.33-10.47 9.6-18.84 30.51-18.84 42.63l-.03 1zm-29.65 0h-1.1c1.17-2.52 1.79-5.2 1.79-8 0-20 4.83-42.04 12.15-49.35 5.17-5.18 7.77-8.38 9.9-12.74 2.64-5.41 3.95-12 3.95-20.91 0-6.82 1.14-11.59 3.37-15.07 1.74-2.7 3.6-4.21 8.91-7.52a31.64 31.64 0 0 0 3.9-2.79c4.61-3.96 6.58-6.2 7.72-9.41 1.43-4.02.93-9.04-1.86-16.02a68.98 68.98 0 0 0-3.99-8.07l-.93-1.7a75.47 75.47 0 0 1-2.64-5c-5.16-10.71-3.77-18.9 7.68-29.78a204 204 0 0 1 26.81-21.55c3.96-2.69 16.8-10.8 19.24-12.5 1.99-1.4 4.33-3.3 7.77-6.3-.02 0 7.23-6.39 9.47-8.3 4.97-4.26 9.09-7.5 13.05-10.15 4.72-3.15 8.97-5.28 12.87-6.32 12.78-3.41 15.6-4.18 21.77-5.97 12.55-3.64 21.96-6.9 28.14-10a45.47 45.47 0 0 1 7.47-2.79c8.66-2.66 12.02-4.1 16.97-8.1 6.78-5.46 13.07-14.25 19.33-27.87 15.97-34.77 19.08-39.39 32.15-49.19 3.14-2.36 6.37-4.1 11.43-6.4l2.33-1.04c11.93-5.35 16.87-8.93 21.1-17.38 1.88-3.77 2.48-6.29 3.37-12.27.78-5.19 1.48-7.56 3.53-10.25 2.57-3.4 7.03-6.27 14.36-9.01 3.37-1.26 7.36-2.5 12.05-3.73 16.33-4.3 25.28-5.36 39.6-5.81 6.9-.22 9.5-.56 12.66-2 1.19-.54 2.36-1.23 3.58-2.11 3.7-2.7 8.14-4.54 13.24-5.67 5.71-1.27 10.69-1.54 18.7-1.45l2.35.02c2.82 0 6.8-1 19.7-4.69 10.83-3.08 15.95-4.31 19.3-4.31.82 0 1.9.13 3.55.41l5.01.9c9.82 1.68 17.44 1.89 25.15-.21 7.98-2.18 14.8-6.77 20.29-14.24V147c-5.47 7.04-12.21 11.42-20.03 13.55-7.88 2.15-15.63 1.94-25.58.23l-5-.9c-1.6-.26-2.64-.39-3.39-.39-3.2 0-8.32 1.22-19.74 4.48-12.35 3.53-16.3 4.52-19.26 4.52l-2.36-.02c-7.94-.1-12.85.17-18.47 1.42-4.97 1.11-9.3 2.9-12.88 5.5a21.4 21.4 0 0 1-3.75 2.22c-3.32 1.5-6 1.87-13.04 2.09-14.25.44-23.13 1.5-39.37 5.77a125.56 125.56 0 0 0-11.95 3.7c-7.17 2.7-11.49 5.46-13.93 8.68-1.9 2.52-2.58 4.76-3.33 9.8-.9 6.08-1.53 8.68-3.47 12.56a30.6 30.6 0 0 1-9.66 11.45c-3.12 2.26-5.95 3.73-11.93 6.4l-2.31 1.04c-5.01 2.27-8.18 3.99-11.25 6.29-12.9 9.68-15.93 14.17-31.85 48.8-6.31 13.76-12.7 22.68-19.6 28.25-5.08 4.1-8.53 5.57-17.3 8.27a44.64 44.64 0 0 0-7.33 2.73c-6.24 3.12-15.7 6.4-28.3 10.06a867.4 867.4 0 0 1-21.8 5.97c-3.77 1.01-7.93 3.1-12.56 6.19a137.35 137.35 0 0 0-12.95 10.07c-2.24 1.92-9.48 8.3-9.48 8.3a98.2 98.2 0 0 1-7.84 6.37c-2.46 1.72-15.32 9.83-19.26 12.5a203 203 0 0 0-26.69 21.45c-11.13 10.58-12.43 18.3-7.47 28.63a74.52 74.52 0 0 0 2.62 4.95l.94 1.7a69.84 69.84 0 0 1 4.03 8.17c2.88 7.2 3.4 12.46 1.89 16.73-1.22 3.43-3.28 5.77-8.02 9.84-1.14.97-2.32 1.8-5.3 3.67-3.92 2.45-5.69 3.89-7.31 6.42-2.13 3.3-3.22 7.89-3.22 14.53 0 9.05-1.34 15.79-4.05 21.34-2.19 4.49-4.85 7.77-10.1 13.01-7.07 7.07-11.85 28.9-11.85 48.65 0 2.8-.58 5.48-1.7 8zm282.54 0h-1.01l-1.1-5.8c-3.08-16.26-4.05-26.2-2.74-37.26.7-5.8.77-9.68.55-15.3-.18-4.45-.17-5.68.19-7.63.78-4.3 3.44-8.53 10.39-16.34 9.07-10.2 12.26-15.41 19.8-30.15 1.35-2.64 2.33-4.47 3.38-6.3.9-1.58 1.82-3.06 2.77-4.5 3.14-4.7 7.03-8.42 16.84-16.81 11.22-9.6 15.5-13.86 18.13-19.13.7-1.4 1.3-2.8 1.93-4.4a206 206 0 0 0 1.49-4.05c3.63-9.94 8.01-13.93 22.9-17.81 4.99-1.3 20.55-5.13 21.38-5.34 16.19-4.1 25.33-7.36 33.48-12.6 5.86-3.77 5.84-3.76 27.66-16.53l2.6-1.52c10.23-6 17.1-10.2 22.73-13.95a149.3 149.3 0 0 0 8.8-6.3 723.7 723.7 0 0 0 6.37-5.08A87.74 87.74 0 0 1 600 342.95v1.12a85.76 85.76 0 0 0-15.49 9.9c.18-.14-4.76 3.84-6.38 5.1a150.3 150.3 0 0 1-8.85 6.35c-5.65 3.76-12.53 7.96-22.78 13.97l-2.6 1.53c-21.8 12.75-21.78 12.74-27.63 16.5-8.27 5.32-17.49 8.61-33.78 12.73-.83.21-16.39 4.04-21.36 5.33-8.03 2.1-13.15 4.5-16.45 7.5-2.66 2.42-4 4.86-5.77 9.7l-1.5 4.07a51.12 51.12 0 0 1-1.96 4.47c-2.72 5.45-7.04 9.75-18.38 19.45-9.73 8.32-13.6 12.02-16.65 16.6a77.18 77.18 0 0 0-2.74 4.45c-1.05 1.81-2.01 3.63-3.35 6.25-7.58 14.81-10.82 20.08-19.96 30.36-6.83 7.7-9.4 11.78-10.15 15.86-.34 1.85-.34 3.04-.17 7.4.22 5.68.14 9.6-.55 15.47-1.3 10.92-.34 20.79 2.73 36.95l1.12 5.99zm-76.59 0h-2.1l1.39-4.3c1.04-3.3 1.93-6.78 2.68-10.4 2.65-12.73 3.27-23.63 3.27-41.3 0-5.71-1.86-9.75-4.13-9.75-2.94 0-6.96 5.61-10.93 17.08C271.14 579.68 258.3 593 238 593c-22.42 0-29.26-1.35-48.42-10.09a87.69 87.69 0 0 1-9.42-5.04c-2.95-1.8-12.78-8.57-14.84-9.72-4.2-2.36-7-2.71-9.72-.99-.63.4-1.26.91-1.9 1.55a57.69 57.69 0 0 1-4.31 3.86 147.88 147.88 0 0 1-3.06 2.44l-1 .8C137.01 582.43 134 587.18 134 597c0 1.02-.02 2.01-.07 3h-2c.05-.99.07-1.98.07-3 0-10.52 3.33-15.78 12.09-22.76a265.61 265.61 0 0 1 2-1.6c.83-.64 1.43-1.13 2.03-1.61a55.76 55.76 0 0 0 4.17-3.74c.74-.73 1.48-1.34 2.24-1.82 3.47-2.2 7-1.75 11.77.93 2.15 1.21 12.03 8 14.9 9.76a85.7 85.7 0 0 0 9.22 4.93C209.29 589.7 215.85 591 238 591c19.25 0 31.49-12.7 41.06-40.33 4.24-12.25 8.66-18.42 12.81-18.42 3.8 0 6.13 5.06 6.13 11.75 0 17.8-.63 28.8-3.3 41.7-.77 3.7-1.68 7.23-2.75 10.6-.4 1.3-.8 2.53-1.19 3.7zm-149.25 0 .5-.94a160.1 160.1 0 0 0 6.53-13.26c2.73-6.29 5.78-9.64 9.24-10.52 3.74-.95 7.15.74 12.56 5.13 5.43 4.4 6.07 4.86 7.73 5.1 1.6.22 4.28 1.14 8.86 2.95 1.3.5 10.78 4.35 13.85 5.55 3.07 1.2 5.85 2.25 8.49 3.18 3.1 1.1 5.98 2.04 8.65 2.81h-3.45c-1.76-.56-3.6-1.18-5.54-1.87a281.2 281.2 0 0 1-8.51-3.19c-3.08-1.2-12.57-5.04-13.86-5.55-4.5-1.78-7.15-2.68-8.63-2.9-1.94-.27-2.53-.7-8.22-5.3-5.17-4.2-8.36-5.78-11.69-4.94-3.1.78-5.94 3.92-8.56 9.95a161 161 0 0 1-6.82 13.8h-1.13zm112.89 0a30.34 30.34 0 0 0 11.27-6.27c1.55-1.36 3.32-3.46 5.34-6.29 1.05-1.46 2.15-3.1 3.41-5.04a349.73 349.73 0 0 0 2.5-3.9l.47-.75.93-1.47a89.17 89.17 0 0 1 3.25-4.86c1.05-1.43 1.82-2.23 2.44-2.46 1.02-.37 1.49.48 1.49 2.04l.01 2.11c.05 6.91-.08 11.32-.7 16.33a48.4 48.4 0 0 1-2.38 10.56h-1.07a46.47 46.47 0 0 0 2.45-10.68c.62-4.96.75-9.33.7-16.2l-.01-2.12c0-.97-.08-1.12-.15-1.1-.36.14-1.05.85-1.97 2.1a88.44 88.44 0 0 0-3.22 4.82l-.92 1.46-.48.75a1268.1 1268.1 0 0 1-2.5 3.92c-1.26 1.95-2.38 3.6-3.44 5.08-2.06 2.88-3.87 5.04-5.5 6.45a30.87 30.87 0 0 1-8.94 5.52h-2.98zm-183.72 0H69.3c3.37-3.43 5.19-8.33 5.19-15 0-18.6-.04-17.35 1.02-20.77.6-1.93 1.5-3.74 3.27-6.63.42-.7 4.92-7.8 6.78-10.86 3.04-4.97 11.04-16.5 12.21-18.56 3.48-6.08 4.72-12.06 4.72-24.18 0-7.85 2.5-14.2 8.1-23.44l2.84-4.63a72.67 72.67 0 0 0 2.49-4.4c1.62-3.15 2.48-5.78 2.62-8.28.2-3.78-1.3-7.29-4.9-10.9-5.13-5.12-8.6-5.43-11.2-1.85-2.12 2.92-3.48 7.74-5.06 16.47-.2 1.03-.82 4.6-.82 4.57-.83 4.67-1.4 7.33-2.1 9.6-1.35 4.42-3.7 7.61-8.36 12.26l-3.26 3.2c-6.38 6.39-9.68 11.51-11.36 19.5l-1.16 5.52c-.87 4.1-1.56 7.04-2.33 9.94-3.67 13.74-9.65 25.97-22.59 44.72-7.68 11.14-11.05 18.87-10.92 23.72h-1c-.12-5.16 3.35-13.05 11.1-24.28 12.87-18.67 18.8-30.8 22.44-44.42.77-2.88 1.45-5.8 2.32-9.89l1.16-5.51c1.73-8.22 5.13-13.5 11.64-20 .63-.64 2.84-2.8 3.25-3.21 4.57-4.54 6.82-7.62 8.12-11.84a81.58 81.58 0 0 0 2.07-9.48l.81-4.57c1.62-8.9 3-13.8 5.24-16.89 3-4.15 7.2-3.78 12.71 1.74 3.8 3.8 5.42 7.58 5.2 11.66-.15 2.66-1.05 5.41-2.73 8.68a73.6 73.6 0 0 1-2.52 4.46l-2.84 4.63c-5.52 9.1-7.96 15.3-7.96 22.92 0 12.28-1.28 18.43-4.85 24.68-1.2 2.1-9.21 13.65-12.22 18.58-1.87 3.06-6.37 10.18-6.78 10.86-1.73 2.82-2.6 4.57-3.17 6.4-1.02 3.28-.98 2.1-.98 20.48 0 6.52-1.7 11.44-4.82 15zM310.09 0h1.06c-.37.9-.77 1.83-1.2 2.82-3.9 9.06-5.45 15.15-5.45 25.18 0 7.64-2.1 11.6-6.64 13.05-3.46 1.1-5.72.98-17.57-.43-11.55-1.36-19.17-1.58-28.16-.14-6.24 2.49-25.91 7.02-32.13 7.02-11.15 0-36.76-2.88-54.12-7.01a22.08 22.08 0 0 0-16.95 2.48c-4.05 2.33-7.09 5.03-13.9 11.97-6.28 6.39-9.53 9.23-13.8 11.5-7.09 3.79-11.22 7.65-13.4 12.27-1.82 3.85-2.33 7.84-2.33 15.29 0 4.4-2.65 6.69-9.45 9.74.1-.05-2.97 1.31-3.84 1.71-8.78 4.06-12.71 8.29-12.71 16.55 0 12.52-4.86 19.22-17.34 27.96l-4.56 3.14c-1.9 1.3-3.3 2.3-4.67 3.3-.92.68-1.79 1.34-2.62 2-7.16 5.62-11 14.54-15.56 33.28-.63 2.57-3.3 14-4.07 17.14a350.44 350.44 0 0 1-5.2 19.33c-1.37 4.5-4.5 15.07-4.96 16.53-1.05 3.4-1.64 4.94-2.46 6.32-.82 1.4-6.85 9.08-12.64 18.27L0 277.98v-1.9l4.58-7.35a270.8 270.8 0 0 1 12.61-18.23c-.3.5 1.35-2.8 2.38-6.12.45-1.44 3.58-12.01 4.95-16.53 1.83-6.03 3.44-12.09 5.19-19.27.76-3.13 3.44-14.56 4.06-17.14 4.62-18.95 8.52-28.02 15.92-33.83.84-.67 1.72-1.33 2.65-2.01 1.38-1.02 2.8-2.01 4.7-3.32l4.54-3.14C73.83 140.57 78.5 134.13 78.5 122c0-8.74 4.2-13.26 13.29-17.45.88-.41 3.96-1.77 3.85-1.73 6.46-2.9 8.86-4.97 8.86-8.82 0-7.6.53-11.7 2.42-15.71 2.29-4.84 6.57-8.85 13.84-12.73 4.15-2.21 7.35-5 14.15-11.93 6.28-6.4 9.36-9.13 13.52-11.53a23.07 23.07 0 0 1 17.69-2.59c17.27 4.12 42.8 6.99 53.88 6.99 6.1 0 25.73-4.53 31.92-7 9.12-1.46 16.83-1.25 28.49.13 11.63 1.38 13.9 1.5 17.15.47 4.06-1.3 5.94-4.85 5.94-12.1 0-10.1 1.56-16.3 6.6-28zm25.12 0h1c.05 5.62.26 11.48.65 19.4.47 9.7.64 14.57.64 21.6 0 9.81-4.68 17.46-13.1 23.16-6.53 4.43-14.94 7.46-24.33 9.33-3.74.54-9.42.56-22.68.23-6.74-.17-9.35-.22-12.39-.22-2.77 0-4.97.43-7.63 1.36-.88.3-4.55 1.74-5.58 2.11-6.55 2.35-13.59 3.53-24.79 3.53-8.1 0-13.58-1.38-22.46-4.9l-3.18-1.25c-12.55-4.87-21.27-5.15-37.18 1.12-11.15 4.39-18.13 9.2-22.28 14.81-3.15 4.26-4.33 7.8-5.94 15.8-1.22 6.09-1.93 8.74-3.5 12.13-1.65 3.53-3.97 5.81-7.07 7.22-2.33 1.07-4.35 1.5-9.32 2.19-9.04 1.27-12.77 3.09-15.61 9.58-3.71 8.48-7.72 13.87-14.22 19.76-2.4 2.18-13.14 11.02-15.91 13.42-8.2 7.1-13.85 17.37-18.7 31.97a258.81 258.81 0 0 0-3.27 10.7c-.01.05-2.26 7.97-2.88 10.1-8.49 28.85-17.88 52.95-26.13 61.2-2.8 2.8-5.06 5.64-10.4 12.96-3.4 4.68-6.23 8.25-8.95 11.1v-1.55c2.74-2.98 5.73-6.82 9.48-11.97 4.03-5.52 6.32-8.4 9.17-11.24 8.07-8.08 17.44-32.14 25.87-60.8.62-2.1 2.86-10.03 2.88-10.08 1.21-4.24 2.21-7.53 3.28-10.74 4.9-14.75 10.63-25.16 19-32.4 2.78-2.42 13.5-11.25 15.89-13.4 6.4-5.8 10.32-11.09 13.97-19.43 1.68-3.83 4.05-6.31 7.2-7.86 2.4-1.17 4.64-1.67 9.53-2.36 4.54-.63 6.5-1.05 8.7-2.06 2.89-1.31 5.03-3.42 6.58-6.73 1.53-3.3 2.23-5.9 3.43-11.9 1.64-8.14 2.85-11.79 6.11-16.2 4.28-5.79 11.41-10.7 22.73-15.16 16.15-6.36 25.13-6.07 37.9-1.11l3.19 1.26c8.77 3.47 14.13 4.82 22.09 4.82 11.09 0 18.02-1.16 24.46-3.47 1-.36 4.68-1.8 5.58-2.11A22.5 22.5 0 0 1 265 72.5c3.05 0 5.67.05 14.07.26 11.53.29 17.2.27 20.83-.25 9.25-1.85 17.54-4.83 23.94-9.17C332 57.8 336.5 50.46 336.5 41c0-7-.17-11.86-.7-22.7-.35-7.26-.55-12.83-.59-18.3zM93.87 0h2.04c-.7 4-1.61 6.82-3.03 9.47-2.33 4.38-2.85 5.75-5.26 13.03a40.46 40.46 0 0 1-1.94 5.03c-2.24 4.66-5.92 8.8-13.07 14.26-8.01 6.13-14.27 16.55-20.03 31.55-2.4 6.23-8.75 25.63-9.64 28.01-2.69 7.16-6.56 12.7-15.63 23.68l-2.68 3.24c-6.02 7.34-9.35 12.07-11.72 17.15-2.3 4.94-7.12 9.9-12.91 14.15v-2.4c5.14-3.94 9.1-8.3 11.1-12.6 2.46-5.27 5.87-10.1 11.98-17.56l2.68-3.26c8.94-10.8 12.72-16.22 15.3-23.1.88-2.33 7.24-21.74 9.65-28.03 5.89-15.31 12.3-26 20.68-32.41 6.92-5.3 10.4-9.2 12.48-13.55.65-1.35 1.16-2.7 1.85-4.79 2.45-7.4 3-8.83 5.4-13.34A27.68 27.68 0 0 0 93.87 0zm9.07 0h1.02c-1.66 8.3-2.91 12.67-4.54 15.26a59.14 59.14 0 0 0-4.1 8.21c-1.27 3-2.44 6.2-3.5 9.4-.38 1.12-.7 2.16-2.41 5.39a251.48 251.48 0 0 0-12.81 13.3c-3.48 3.96-5.95 7.27-7.15 9.66-.95 1.9-2.06 5.99-3.61 12.97-.64 2.9-3.65 17.15-4.51 21.07-3.63 16.45-6.63 26.69-9.9 32-7.66 12.45-10.64 15.71-37.08 41.1A69.78 69.78 0 0 1 0 179.21v-1.15a69.39 69.39 0 0 0 13.65-10.42c26.4-25.33 29.32-28.55 36.92-40.9 3.2-5.18 6.18-15.37 9.78-31.7.86-3.91 3.87-18.16 4.51-21.06 1.57-7.09 2.7-11.2 3.7-13.2 1.24-2.5 3.76-5.86 7.29-9.89.9-1.03 1.86-2.1 2.86-3.18 2.4-2.6 4.96-5.22 7.53-7.76.9-.88 1.73-1.7 3.37-3.4a129.02 129.02 0 0 1 4.78-13.46 60.07 60.07 0 0 1 4.19-8.35c1.52-2.44 2.74-6.71 4.36-14.74zM83.71 0h1.1c-2.09 4.74-6.03 8.92-11.42 12.3-7.2 4.52-16.5 7.2-24.39 7.2-8.9 0-11.8 7-11.74 21.52 0 1.7.04 3.17.12 5.99.1 3.3.12 4.45.12 5.99 0 5.73-.76 11.3-2.01 16.5a66.67 66.67 0 0 1-2.15 6.97 2597.76 2597.76 0 0 1-7 15.86 4270.8 4270.8 0 0 1-19.9 43.87A54.64 54.64 0 0 1 0 147v-1.65a54.87 54.87 0 0 0 5.55-9.57A4269.82 4269.82 0 0 0 30.7 79.97c.53-1.2.99-2.23 2.44-5.9A69.23 69.23 0 0 0 36.5 53c0-1.52-.03-2.66-.12-5.95-.08-2.83-.12-4.31-.12-6.01-.03-6.79.53-11.62 2.07-15.34 1.94-4.68 5.39-7.19 10.67-7.19 7.7 0 16.81-2.63 23.86-7.05C77.93 8.27 81.66 4.38 83.7 0zm282.63 0h1.01c1.86 10.02 2.18 12.67 2.32 18.3a123.43 123.43 0 0 1 .37 27.83c-.96 8.78-3.1 16.01-6.63 21.15-11.34 16.5-39.8 29.22-66.41 29.22-5.09 0-10.47.28-16.31.83a413.8 413.8 0 0 0-24.37 3.16c-21.56 3.26-27.66 4.01-36.32 4.01-6.92 0-12.2-1.05-21.69-3.9l-2.78-.83c-1.39-.41-2.54-.74-3.65-1.02-8-2.05-14.22-2.04-21.7.72a16.32 16.32 0 0 0-9.17 8.18c-1.6 3.05-2.5 6.06-4.02 12.83-1.5 6.64-2.34 9.52-3.99 12.64a16.16 16.16 0 0 1-9.85 8.36 104.8 104.8 0 0 0-9.5 3.42c-6.55 2.8-10.1 5.57-13.8 10.47-1.33 1.75-1.03 1.3-5.43 7.9-1.98 2.97-4.66 5.8-8.48 9.14-2.01 1.76-10.71 8.83-12.88 10.7-7.37 6.35-12.58 12.14-16.63 19.14-4.22 7.3-7.8 18.3-11.28 33.26-.87 3.73-1.72 7.64-2.64 12.14l-1.18 5.8-1.09 5.45c-1.8 8.96-2.77 13.28-3.77 16.26-6.8 20.44-17.26 42.16-27.13 51.2-5.11 4.7-8.1 7.07-11.1 8.86-.9.54-1.84 1.04-2.92 1.57-.44.22-9.6 4.4-14.1 6.66l-1.22.62v-1.13l.78-.39c4.52-2.26 13.67-6.44 14.1-6.65a41.19 41.19 0 0 0 2.84-1.54c2.94-1.75 5.88-4.09 10.94-8.73 9.71-8.9 20.1-30.51 26.87-50.79.97-2.92 1.94-7.22 3.73-16.13l1.1-5.46a490.5 490.5 0 0 1 3.82-17.96c3.5-15.06 7.1-26.14 11.39-33.54 4.11-7.11 9.4-12.98 16.83-19.4 2.19-1.88 10.88-8.95 12.88-10.7 3.77-3.28 6.39-6.05 8.3-8.93 4.43-6.64 4.12-6.18 5.47-7.96 3.8-5.03 7.5-7.91 14.21-10.78 2.61-1.12 5.74-2.24 9.59-3.46a15.17 15.17 0 0 0 9.27-7.86c1.59-3.02 2.42-5.85 4.03-12.99 1.41-6.27 2.32-9.33 3.98-12.48a17.31 17.31 0 0 1 9.7-8.66c7.7-2.83 14.1-2.84 22.3-.75 1.12.29 2.28.61 3.68 1.03l3.73 1.11c8.47 2.54 13.66 3.58 20.46 3.58 8.59 0 14.67-.75 36.18-4a414.64 414.64 0 0 1 24.41-3.17c5.88-.54 11.29-.83 16.41-.83 26.3 0 54.45-12.58 65.59-28.78 3.42-4.98 5.5-12.06 6.46-20.7.84-7.74.73-16.02.02-23.9a136.2 136.2 0 0 0-.57-5.12c0-4.47-.3-6.94-2.16-17zM18.88 0h1.03C18 7.57 17.15 10.18 14.46 16.2c-1.95 4.37-2.67 9.19-2.42 14.89.2 4.33.71 7.7 2.28 16.13 1.09 5.88 1.57 8.77 1.94 12.2.96 8.9.24 16.08-2.8 22.79A463.4 463.4 0 0 1 0 109.43v-2.12a465 465 0 0 0 12.54-25.52c2.97-6.52 3.67-13.53 2.72-22.27-.36-3.4-.84-6.26-1.93-12.12-1.57-8.47-2.1-11.88-2.29-16.27-.26-5.84.48-10.81 2.5-15.33 2.64-5.9 3.48-8.47 5.34-15.8zm280.47 0a70.78 70.78 0 0 1-4.91 11.24c-2.56 4.7-4.01 8.45-4.86 11.98l-.4 1.8-.28 1.45a5.28 5.28 0 0 1-.74 2.07c-.74 1.03-1.93 1.28-5.13 1.25.92 0-9.85-.29-15.03-.29-10.2 0-18.45.82-29.46 2.56-16.87 2.66-17.73 2.77-23.66 2.52a42.57 42.57 0 0 1-8-1.09c-17.7-4.16-46.18-5.86-54.72-3.01-2.72.9-5.88 2.8-9.52 5.59a112.37 112.37 0 0 0-6.54 5.48c-1.4 1.25-9.17 8.5-10.78 9.84-1.45 1.2-8.18 7.42-8.85 8.02a114.65 114.65 0 0 1-4.55 3.9c-4.99 4.03-8.9 6.2-11.92 6.2-3.52.05-4.32 0-5.14-.4-1.13-.56-1.5-1.72-1.13-3.57.74-3.63 4.47-10.84 12.84-24.8 5.69-9.48 9.42-18 11.78-26.2 1.45-5.04 1.94-7.4 2.97-14.54h1.01c-1.05 7.3-1.54 9.7-3.01 14.82-2.39 8.28-6.16 16.89-11.9 26.44-8.3 13.84-12 21.01-12.7 24.48-.3 1.45-.08 2.14.59 2.47.6.3 1.35.35 3.48.3 3.92 0 7.69-2.1 12.5-5.98a114.6 114.6 0 0 0 4.51-3.86c.66-.59 7.41-6.83 8.88-8.05 1.59-1.33 9.34-8.55 10.75-9.82 2.4-2.15 4.55-3.96 6.6-5.53 3.72-2.85 6.97-4.8 9.81-5.74 8.76-2.92 37.41-1.22 55.27 2.99 2.57.6 5.14.95 7.81 1.06 5.84.25 6.7.14 23.47-2.51 11.05-1.75 19.36-2.57 29.6-2.57 5.2 0 15.99.3 15.05.29 2.87.03 3.84-.17 4.3-.83.23-.32.4-.8.58-1.7l.28-1.43.4-1.85c.88-3.6 2.36-7.44 4.96-12.22A69.5 69.5 0 0 0 298.29 0h1.06zm-8.59 0c-5.91 17.94-9.55 22-19.76 22-4.5 0-10.22.32-28.69 1.5l-1.53.1c-15.6.99-23.47 1.4-28.78 1.4-5.35 0-13.24-.96-28.86-3.28l-1.54-.23C163.18 18.75 157.47 18 153 18c-4.45 0-7.3 1.01-10.96 3.34-.1.06-1.8 1.17-2.3 1.47-2.43 1.5-4.32 2.19-6.74 2.19-2.8 0-4.11-1.46-4.11-4.22 0-1.04.16-2.29.5-4.1.16-.82.9-4.4 1.07-5.32.8-4.11 1.3-7.68 1.47-11.36h2c-.17 3.82-.68 7.5-1.5 11.75-.19.94-.92 4.5-1.07 5.31a21.04 21.04 0 0 0-.47 3.72c0 1.7.46 2.22 2.11 2.22 1.99 0 3.55-.57 5.7-1.9.47-.28 2.15-1.37 2.26-1.44C144.92 17.14 148.12 16 153 16c4.62 0 10.3.74 28.9 3.51l1.53.23C198.93 22.04 206.8 23 212 23c5.25 0 13.11-.41 28.65-1.4l1.54-.1C260.73 20.32 266.43 20 271 20c8.95 0 12.15-3.4 17.66-20h2.1zM141.51 0h1.13c-2.06 3.86-2.63 5.1-2.77 6.19-.15 1.12.42 1.64 2.32 1.96 1.8.3 3.85.35 10.81.35 6.02 0 13 .56 21.35 1.62 3.95.5 8.03 1.1 13.13 1.89 24 3.7 22.5 3.49 26.83 3.49 24.02 0 51.83-2.24 60.45-6.94 2.88-1.57 5.05-4.49 6.6-8.56h1.07c-1.64 4.47-3.98 7.69-7.2 9.44-8.83 4.82-36.67 7.06-60.92 7.06-4.41 0-2.84.22-26.98-3.5-5.1-.8-9.17-1.38-13.1-1.88-8.31-1.06-15.26-1.62-21.23-1.62-7.04 0-9.1-.05-10.97-.37-2.38-.4-3.38-1.32-3.15-3.07.16-1.22.69-2.41 2.63-6.06zm76.4 0c5.69 1.64 10.37 2.5 14.09 2.5 9.59 0 16.7-.71 22.4-2.5h2.98C251.12 2.53 243.2 3.5 232 3.5c-4.5 0-10.32-1.21-17.53-3.5h3.45zM70.69 0c-2.87 3.27-6.95 5.39-12.02 6.53-3.98.89-7.5 1.08-12.92 1A97.24 97.24 0 0 0 44 7.5c-5.37 0-8.86-1.24-10.1-4.97A8.6 8.6 0 0 1 33.5 0h.99c.02.82.14 1.56.36 2.22C35.91 5.39 39.02 6.5 44 6.5l1.76.02c5.35.09 8.8-.1 12.69-.97C62.95 4.54 66.63 2.74 69.3 0h1.37zM0 207.87c7.31-.16 11.5 3.33 11.5 11.13 0 11.41-5.05 28.35-11.5 41.5v-2.3c5.93-12.72 10.5-28.47 10.5-39.2 0-7.18-3.7-10.3-10.5-10.13v-1zm0 7.05c1.23.14 2.18.58 2.87 1.31 1.4 1.48 1.6 3.72 1.16 7.58l-.16 1.3A28.93 28.93 0 0 0 3.5 229c0 3.2-1.48 9.52-3.5 15.9v-3.45c1.49-5.13 2.5-9.87 2.5-12.45 0-.98.08-1.75.37-4.02l.16-1.29c.42-3.56.24-5.59-.88-6.77-.5-.53-1.21-.87-2.15-1v-1zM0 410.9v-1.47a21.67 21.67 0 0 0 2.97-4.7c1.32-2.7 2.68-6.28 4.56-11.89 7.85-23.55 7.83-26.6.25-30.4-2.25-1.12-4.8-1.43-7.78-.91v-1.02a13.1 13.1 0 0 1 8.22 1.04c8.24 4.12 8.26 7.6.25 31.6-1.88 5.66-3.25 9.27-4.6 12.02A20.82 20.82 0 0 1 0 410.9zM33.64 452c1.68 0 3.04-.23 8.34-1.31l2.38-.47c8.26-1.57 12.72-1.3 14.53 2.33 1.38 2.75-.47 5.86-4.75 9.68a75.6 75.6 0 0 1-5.08 4.07c-.94.7-4.89 3.59-5.79 4.27-1.86 1.4-2.97 2.37-3.47 3.03a19.08 19.08 0 0 0-2.89 5.5c.07-.2-4.02 13.65-6.96 22.22-2.7 7.85-5.56 10.72-8.82 8.59-2.11-1.4-3.66-4.24-6.6-11.03-1.98-4.62-2.5-5.76-3.4-7.4-4.55-8.18-3.9-23.9-.05-32.87a9.6 9.6 0 0 1 6.98-5.96c2.59-.66 4.86-.75 11.78-.67l3.8.02zm0 2c-1.13 0-2.09 0-3.82-.02-12.07-.13-14.83.57-16.9 5.41-3.63 8.47-4.26 23.55-.05 31.12.96 1.73 1.48 2.88 3.5 7.58 2.72 6.3 4.24 9.08 5.86 10.14 1.64 1.08 3.5-.8 5.82-7.55a682.9 682.9 0 0 0 6.97-22.24 21.03 21.03 0 0 1 3.18-6.04c.65-.87 1.85-1.9 3.86-3.43.92-.7 4.87-3.57 5.8-4.27 2.02-1.5 3.6-2.77 4.95-3.97 3.63-3.23 5.09-5.7 4.3-7.28-1.21-2.42-5.07-2.65-12.38-1.27l-2.35.47c-5.49 1.11-6.86 1.35-8.74 1.35zm345.63 146c-3.45-12.26-3.77-14.13-3.77-19 0-3.33-.13-6.27-.43-11.34-.63-10.33-.65-13.5.26-17.07 1.21-4.74 4.21-7.1 9.67-7.1h26c4.08 0 5.19 1.85 5.93 7.11.1.79.13.97.19 1.32.84 5.35 2.8 7.58 8.88 7.58 3.64 0 5.54.4 6.43 1.37.76.83.76 1.44.36 3.93-.85 5.26.5 8.85 7.5 13.8 6.32 4.45 11.63 5.36 16.55 3.37 3.8-1.54 6.73-4.16 11.92-10l1.1-1.23 1.09-1.23a75.6 75.6 0 0 1 2.7-2.86 35.81 35.81 0 0 1 9.57-6.73c1.52-.76 1.72-.86 5.66-2.63 6.1-2.73 9.01-4.5 11.74-7.62 2.63-3 4.67-4.85 6.7-6.04 3.18-1.85 5.46-2.13 13.68-2.13 5.98 0 10.56-4.32 18-14.99l2.82-4.03c1.06-1.5 1.94-2.7 2.79-3.79 7.87-10.12 19.38-10.4 30.74.96 5.54 5.53 10.17 19.43 13.64 38.51 2.5 13.75 4.18 29.46 4.47 39.84h-1c-.3-10.32-1.96-25.97-4.45-39.66-3.43-18.87-8.02-32.65-13.36-37.99-10.95-10.95-21.76-10.68-29.26-1.04-.83 1.07-1.7 2.26-2.75 3.75l-2.81 4.02c-7.65 10.95-12.38 15.42-18.83 15.42-8.04 0-10.21.26-13.17 2-1.92 1.12-3.9 2.9-6.45 5.83-2.86 3.26-5.87 5.09-12.09 7.88a103.35 103.35 0 0 0-5.62 2.6 34.84 34.84 0 0 0-9.32 6.54 74.67 74.67 0 0 0-3.75 4.05l-1.1 1.24c-5.28 5.95-8.29 8.64-12.28 10.25-5.26 2.13-10.92 1.17-17.5-3.48-7.33-5.17-8.82-9.15-7.92-14.77.34-2.12.34-2.6-.1-3.1-.64-.69-2.34-1.04-5.7-1.04-6.63 0-8.96-2.63-9.87-8.42l-.2-1.34c-.67-4.82-1.53-6.24-4.93-6.24h-26c-5 0-7.6 2.04-8.7 6.34-.88 3.43-.85 6.57-.23 16.76a177 177 0 0 1 .43 11.4c0 4.78.32 6.63 3.81 19h-1.04zm13.68 0c-1.31-6.58-1.61-10.71-1.36-14.84.04-.7.1-1.44.18-2.38l.23-2.56c.34-3.81.5-6.97.5-11.22 0-4.94 1.46-7.76 4.21-8.42 2.38-.58 5.56.54 9.2 3 6.64 4.52 13.99 13.07 16.55 19.23 4.77 11.44 14.12 15.69 33.54 15.69 8.6 0 14.32-2.35 20.67-7.88 1.45-1.26 15.06-15 21-20 7.21-6.07 11.77-7.59 20.62-8.32 5.52-.45 7.98-.9 11.44-2.36 4.58-1.95 9.36-5.48 14.9-11.29 7.43-7.76 13.25-8.92 17.47-4.3 3.32 3.63 5.46 10.58 6.82 20.24.73 5.17.94 7.74 1.58 17.38.25 3.75.17 5.32-.92 18.03h-1c1.09-12.7 1.17-14.28.92-17.97-.64-9.6-.85-12.16-1.57-17.3-1.33-9.47-3.43-16.27-6.56-19.7-3.76-4.11-8.93-3.08-16 4.32-5.65 5.9-10.54 9.5-15.25 11.5-3.58 1.53-6.13 1.99-11.6 2.44-8.8.72-13.17 2.18-20.2 8.1-5.9 4.96-19.5 18.7-21 19.99-6.52 5.68-12.47 8.12-21.32 8.12-19.78 0-29.5-4.42-34.46-16.3-2.49-5.97-9.71-14.38-16.2-18.79-3.42-2.32-6.36-3.35-8.4-2.86-2.2.53-3.44 2.92-3.44 7.45 0 4.28-.16 7.47-.5 11.31l-.23 2.56c-.09.93-.14 1.65-.19 2.35-.24 4.08.06 8.18 1.39 14.78h-1.02zm113.75 0c2.52-3.26 8.93-11.79 10.9-14.3 5.48-6.98 13.05-12.38 19.4-13.94 7.01-1.71 11.5 1.45 11.5 9.24 0 4.02-.04 5.16-.74 19h-1c.7-13.85.74-15 .74-19 0-7.12-3.86-9.83-10.26-8.26-6.11 1.5-13.5 6.77-18.85 13.57-1.86 2.36-7.65 10.07-10.43 13.69h-1.26zm-9.86-338.96c3.44 2.71 7 5.1 11.44 7.75 1.06.64 8.42 4.9 10.35 6.1 11.27 7 15 13.35 12.35 25.33-1.45 6.52-4.53 11.1-9.39 14.44-3.83 2.63-8.07 4.26-16.08 6.56-11.97 3.45-13.68 3.99-18.82 6.28a60.18 60.18 0 0 0-7.81 4.18c-11.11 7.07-19.1 7.7-27.96 3.28-3.56-1.77-17.2-11-17.2-11.01a101.77 101.77 0 0 0-5.2-3.07c-16.04-8.83-34.27-24.16-34.52-31.85-.11-3.46 1.99-6.57 6.28-10.26 1.03-.9 2.18-1.81 3.68-2.95.72-.55 3.38-2.56 3.94-3 4.47-3.4 7.18-5.79 9.32-8.45 11.12-13.82 26.55-28.68 34.36-32.28 12.06-5.54 19.84-5.77 27.37.12 3.25 2.54 5.65 6.54 8.58 13.35.29.65 2.3 5.45 2.88 6.74 1.62 3.65 2.9 5.8 4.24 6.94.72.6 1.45 1.2 2.2 1.8zm-3.49-.28c-1.63-1.39-3.03-3.74-4.77-7.65-.58-1.3-2.6-6.12-2.88-6.76-2.81-6.5-5.08-10.3-7.98-12.56-6.83-5.35-13.85-5.15-25.3.12-7.45 3.42-22.7 18.12-33.64 31.72-2.27 2.82-5.08 5.3-9.67 8.79l-3.94 2.98a79.98 79.98 0 0 0-3.59 2.88c-3.87 3.33-5.67 6-5.58 8.69.21 6.64 18.14 21.72 33.48 30.15 1.76.97 3.5 2 5.3 3.13.12.08 13.61 9.22 17.03 10.92 8.22 4.1 15.46 3.52 26-3.18a62.17 62.17 0 0 1 8.07-4.31c5.25-2.35 7-2.9 19.08-6.38 7.8-2.24 11.9-3.82 15.5-6.3 4.44-3.04 7.23-7.18 8.56-13.22 2.44-11.02-.83-16.6-11.45-23.2-1.9-1.18-9.23-5.42-10.32-6.08-4.5-2.69-8.13-5.12-11.64-7.9-.77-.6-1.52-1.21-2.26-1.84zM87.72 241.6c4.3-2.98 7.88-5 12.14-6.95.84-.4 1.73-.78 2.78-1.24l4.37-1.88a164.3 164.3 0 0 0 17.74-8.96 320.67 320.67 0 0 1 27.87-14.5c4.22-1.95 21.89-9.84 21.17-9.52 19.17-8.62 28.1-6.93 49.5 8.05 7.91 5.54 13.24 13.25 16.45 22.66 3.02 8.83 3.76 16.51 3.76 27.75 0 8.32-.66 12.95-3.68 18.97-4.18 8.36-12.3 16.14-25.58 23.47-24.45 13.49-38.83 27.55-52.83 47.84-8.83 12.8-47.76 44.21-65.16 54.15C75.04 413.55 48.89 423.5 31 423.5c-10.05 0-14.67-4.78-14.76-13.37-.07-6.32 2.06-13.73 6.3-24.32 2.95-7.37 2.02-12.9-2.16-22.29-3.19-7.17-3.88-9.14-3.88-12.52 0-3.35 1.87-6.9 5.52-11.07 2.61-3 3.5-3.83 11.9-11.5 5.09-4.66 8.08-7.6 10.7-10.75 9.46-11.36 12.62-19.47 17.9-44.78 3.12-15.05 6.63-20.28 15.12-25.25.8-.47 3.95-2.25 4.7-2.68a76.66 76.66 0 0 0 5.38-3.38zm.56.82a77.63 77.63 0 0 1-5.44 3.43l-4.7 2.67c-8.23 4.82-11.57 9.81-14.65 24.6-5.3 25.45-8.51 33.7-18.1 45.21-2.66 3.19-5.68 6.16-10.8 10.84-8.36 7.64-9.24 8.48-11.82 11.42-3.5 4.01-5.27 7.36-5.27 10.42 0 3.18.68 5.1 3.8 12.12 4.27 9.6 5.24 15.37 2.16 23.07-4.18 10.47-6.29 17.78-6.22 23.93.08 8.06 4.26 12.38 13.76 12.38 17.67 0 43.68-9.9 64.75-21.93 17.28-9.88 56.1-41.2 64.84-53.85 14.08-20.42 28.57-34.59 53.17-48.16 13.12-7.23 21.09-14.87 25.17-23.03 2.92-5.86 3.57-10.35 3.57-18.53 0-11.13-.74-18.73-3.7-27.43-3.15-9.22-8.36-16.75-16.09-22.16-21.13-14.8-29.7-16.42-48.5-7.95.7-.32-16.96 7.56-21.17 9.5-1.7.8-3.3 1.55-4.86 2.3a319.68 319.68 0 0 0-22.93 12.17 165.3 165.3 0 0 1-17.85 9.01l-4.37 1.88c-1.04.45-1.92.84-2.76 1.23a74.56 74.56 0 0 0-11.99 6.86zm-7.6 12.2c7.7-6.25 12.3-8.17 23.68-11.27 6.12-1.67 9.12-2.95 12.31-5.72 3.8-3.3 7.47-4.52 15.86-6.1 2.75-.52 3.67-.7 5.06-1.02 5.48-1.24 9.48-2.93 13.1-5.89 10.42-8.53 25.4-14.11 36.31-14.11 5.33 0 16.77 7.58 25.74 17.16 10.73 11.46 15.96 23.27 12.73 32.5-3.18 9.1-11.39 18.57-23.03 27.86-8.44 6.73-18.36 13-25.22 16.43-3.72 1.86-6.59 4.88-9.77 9.99-.69 1.1-11.1 20.25-16.03 27.83-5.62 8.65-15.4 17.36-30.23 27.96a552.58 552.58 0 0 1-9.2 6.42c-.13.09-6.81 4.65-8.6 5.89-6.47 4.46-10.35 7.35-13.05 9.83-11.64 10.67-37.14 15.54-43.7 8.98-1.96-1.96-2.2-4.06-1.95-10.52.37-9.42-.5-14.5-4.95-20.51a34.09 34.09 0 0 0-7.04-6.92c-3.93-2.95-6.07-6.11-6.56-9.49-.97-6.61 3.87-13.06 14.17-21.69 1.58-1.32 6.67-5.44 7.09-5.78a48.03 48.03 0 0 0 5.23-4.77c4.1-4.63 5.85-9.55 7.8-20.07a501.52 501.52 0 0 0 .8-4.37c.33-1.87.6-3.3.88-4.73.74-3.78 1.5-7.18 2.4-10.63 1-3.78 1.38-5.5 2.36-10.37.6-3.02.93-4.21 1.56-5.47 1.22-2.45 1.27-2.5 12.25-11.42zm.64.78c-10.77 8.74-10.88 8.84-12 11.08-.58 1.16-.88 2.3-1.47 5.22-.98 4.89-1.36 6.63-2.37 10.44-.9 3.43-1.65 6.8-2.39 10.56a339.79 339.79 0 0 0-1.29 6.95l-.39 2.15c-1.98 10.68-3.77 15.74-8.04 20.54a48.77 48.77 0 0 1-5.34 4.88c-.42.34-5.5 4.47-7.07 5.78-10.04 8.4-14.72 14.65-13.83 20.78.45 3.1 2.44 6.03 6.17 8.83 3 2.25 5.39 4.62 7.24 7.12 4.63 6.24 5.52 11.52 5.15 21.15-.25 6.14-.01 8.1 1.66 9.78 6.1 6.1 31.02 1.33 42.31-9.02 2.75-2.52 6.66-5.43 13.16-9.92l8.6-5.89c3.63-2.48 6.45-4.44 9.19-6.4 14.73-10.54 24.44-19.18 29.97-27.7 4.9-7.54 15.31-26.68 16.02-27.8 3.27-5.26 6.26-8.41 10.18-10.37 6.79-3.4 16.65-9.63 25.03-16.32 11.52-9.18 19.61-18.53 22.72-27.4 3.07-8.78-2.02-20.27-12.52-31.49-8.8-9.4-20.04-16.84-25.01-16.84-10.67 0-25.43 5.5-35.68 13.89-3.76 3.07-7.9 4.81-13.5 6.09-1.41.32-2.35.5-5.11 1.02-8.21 1.55-11.76 2.73-15.38 5.88-3.34 2.9-6.45 4.22-12.7 5.92-11.26 3.07-15.75 4.94-23.31 11.09zM212 251.85c0 7.56-.6 10.92-2.6 14.3-1.1 1.84-7.66 10.05-8.6 11.3-5.96 7.94-9.33 10.28-17.26 13.76-1.34.58-2.2 1-3.03 1.5-.55.33-1.2.66-2 1.02-.71.33-4.46 1.9-5.52 2.39-6.05 2.78-8.99 5.8-8.99 10.73 0 10.97-18.95 36.12-34.51 44.87-8.18 4.6-21.3 9.36-32.78 11.86-13.33 2.9-22.49 2.48-24.62-2.32-1.32-2.97-4.4-4.26-11.98-5.81l-.6-.12c-4.84-.99-6.94-1.55-9.03-2.64-2.92-1.5-4.48-3.7-4.48-6.84 0-2.74 1.08-5.77 3.25-9.67.85-1.53 1.82-3.13 3.23-5.35-.16.25 2.83-4.4 3.67-5.76 6.69-10.7 9.85-18.5 9.85-27.22 0-18.41 11.22-33.37 27.5-42.86 5.22-3.05 9.23-3.31 15.2-2.12 5.04 1 6.05.9 7.43-1.52 4.5-7.85 7.04-9.5 15.87-9.5 3.93 0 6.97-.98 10.47-3.16 1.56-.97 8.67-6.17 10.99-7.68 9.2-5.98 11.34-7 25.2-11.95 6.95-2.48 15.18 1.28 22.33 9.12 6.55 7.19 11.01 16.61 11.01 23.67zm-2 0c0-6.5-4.25-15.48-10.49-22.32-6.67-7.32-14.16-10.74-20.17-8.59-13.73 4.9-15.73 5.85-24.8 11.75-2.24 1.46-9.37 6.68-11.01 7.7-3.8 2.36-7.2 3.46-11.53 3.46-8.08 0-9.98 1.23-14.13 8.5-1.1 1.91-2.51 2.88-4.35 3.09-1.3.14-1.9.05-5.22-.61-5.53-1.1-9.07-.88-13.8 1.88-15.72 9.17-26.5 23.55-26.5 41.14 0 9.2-3.28 17.29-10.15 28.28l-3.68 5.77c-1.39 2.19-2.35 3.77-3.17 5.25-2.02 3.63-3 6.38-3 8.7 0 4.19 2.87 5.67 11.9 7.52l.61.12c8.27 1.7 11.7 3.13 13.4 6.95 3.17 7.14 36 0 54.6-10.46 14.98-8.43 33.49-32.99 33.49-43.13 0-5.9 3.47-9.48 10.16-12.55 1.1-.5 4.85-2.08 5.52-2.38.74-.34 1.32-.64 1.8-.93.92-.55 1.85-1 3.25-1.62 7.65-3.35 10.75-5.5 16.47-13.12 1.02-1.36 7.47-9.42 8.47-11.11 1.79-3.01 2.33-6.06 2.33-13.3zm-37.18-22.4c.15-.1 2.4-1.51 2.95-1.84.96-.57 1.7-.94 2.43-1.17 2.57-.83 5.06-.1 11.04 3.12 14.86 8 19.43 22.87 9.18 38.71-4.04 6.24-9.37 9-18.72 11.11-.85.2-1.2.27-3.13.68-6.04 1.29-8.78 2.08-11.6 3.65-3.63 2.02-6.09 4.98-7.5 9.44-7.87 24.93-19.72 43.34-36.28 50.31-16.45 6.93-21.13 8.53-27.98 8.89-4.94.25-9.8-.65-15.4-2.89a44.45 44.45 0 0 1-5.64-2.6c-4.02-2.33-5.14-4.74-4.5-9.31.3-2.13 3.77-15.53 4.84-20.65.63-3.05 1.19-6.14 1.75-9.69a464.04 464.04 0 0 0 1.35-8.9c1.42-9.41 2.5-14.27 4.49-18.65 2.46-5.43 6.13-9.03 11.72-11.13 6.59-2.47 10.54-3.1 18.03-3.53 4.75-.27 6.68-.64 9-2.05.61-.37 1.22-.81 1.82-1.33a30.61 30.61 0 0 0 3.37-3.4c.59-.69 2.38-2.9 2.63-3.19 3.36-4 6.3-5.53 12.33-5.53 3.94 0 5.9-.92 8.18-3.36-.17.18 2.75-3.14 3.85-4.22a30.95 30.95 0 0 1 6.79-5c1.5-.83 3.15-1.62 4.99-2.38a64.92 64.92 0 0 0 10.01-5.1zm-14.52 8.34a29.95 29.95 0 0 0-6.57 4.84 116.68 116.68 0 0 0-3.82 4.2c-2.46 2.63-4.68 3.67-8.91 3.67-5.72 0-8.39 1.39-11.57 5.17-.23.28-2.03 2.5-2.63 3.2a31.6 31.6 0 0 1-3.47 3.51c-.65.55-1.3 1.03-1.96 1.43-2.5 1.51-4.55 1.9-9.47 2.19-7.39.42-11.25 1.04-17.72 3.47-5.34 2-8.82 5.4-11.17 10.6-1.93 4.27-3 9.07-4.41 18.39l-.65 4.34-.7 4.57c-.57 3.56-1.12 6.67-1.76 9.73-1.08 5.18-4.54 18.53-4.83 20.59-.59 4.17.35 6.18 4.01 8.3 1.35.77 3.1 1.58 5.52 2.55 5.46 2.18 10.18 3.05 14.97 2.8 6.69-.34 11.32-1.93 27.65-8.8 16.21-6.83 27.92-25.01 35.71-49.7 1.49-4.7 4.12-7.86 7.97-10 2.93-1.63 5.74-2.45 11.87-3.76 1.92-.4 2.28-.49 3.12-.68 9.12-2.06 14.24-4.7 18.1-10.67 9.92-15.34 5.55-29.55-8.82-37.29-5.75-3.1-8.03-3.76-10.25-3.05-.65.2-1.33.54-2.23 1.08-.55.32-2.77 1.72-2.93 1.82a65.91 65.91 0 0 1-10.16 5.17c-1.8.75-3.42 1.52-4.89 2.33zm-42.39 32.72c16.15-2.87 26.36-.97 32.47 6.16 5.08 5.93 1.13 21.42-5.93 35.55-4.79 9.58-10.6 16.21-23.16 25.19-14.15 10.1-35.5 12.2-40.71 3.85-1.86-2.97-2.1-8.14-1.06-15.73.78-5.68 1.86-10.71 4.73-22.98l.12-.51c1.59-6.8 2.37-10.31 3.14-14.14 1.45-7.25 3.74-11.47 7.26-13.74 2.81-1.8 5.53-2.28 12.33-2.62 5.33-.27 7.56-.46 10.81-1.03zm.18.98c-3.3.59-5.56.78-10.94 1.05-6.62.33-9.23.78-11.84 2.46-3.25 2.1-5.42 6.09-6.82 13.1-.77 3.84-1.56 7.35-3.15 14.17l-.12.5c-2.86 12.24-3.93 17.26-4.7 22.9-1.03 7.36-.79 12.36.9 15.07 4.82 7.7 25.54 5.67 39.29-4.15 12.43-8.88 18.13-15.39 22.84-24.81 6.86-13.72 10.75-29 6.07-34.45-5.84-6.81-15.7-8.65-31.53-5.84zM132 276.5c7.12 0 10.66 3.08 11.25 8.7.42 4.02-.43 8.14-2.77 15.94-2.56 8.52-18.36 25.38-27.2 31.28-7.01 4.67-20.02 5.67-26.57.99-3.99-2.85-3.53-12.08.02-26.46.68-2.75 1.47-5.65 2.37-8.76a412.6 412.6 0 0 1 3.05-10.14l.37-1.2c1.48-4.8 5.1-7.75 10.73-9.27 4.4-1.2 9.54-1.5 17.48-1.33l3.89.1c3.87.11 5.42.15 7.38.15zm0 1c-1.97 0-3.53-.04-7.41-.15l-3.88-.1c-7.85-.17-12.92.13-17.2 1.3-5.32 1.43-8.67 4.16-10.03 8.6a1277.83 1277.83 0 0 1-1.6 5.21c-.68 2.2-1.27 4.17-1.82 6.1-.9 3.1-1.68 5.99-2.36 8.73-3.43 13.88-3.87 22.93-.4 25.4 6.17 4.42 18.73 3.45 25.42-1 8.66-5.78 24.33-22.49 26.8-30.73 2.3-7.67 3.14-11.71 2.73-15.56-.53-5.1-3.64-7.8-10.25-7.8zm-17.79 7a31.3 31.3 0 0 1 8.57 1.4c5.42 1.78 8.72 5.03 8.72 10.1 0 9.59-9.51 17.2-22.34 21.47-9.82 3.28-13.62-1.79-11.66-16.54.84-6.28 3.82-10.67 8.24-13.46a20.38 20.38 0 0 1 8.47-2.97zm-.6 1.08a19.39 19.39 0 0 0-7.34 2.73c-4.18 2.64-6.98 6.78-7.77 12.76-1.89 14.11 1.36 18.45 10.34 15.46C121.3 312.37 130.5 305 130.5 296c0-4.56-2.98-7.5-8.03-9.15a28.05 28.05 0 0 0-8.2-1.35c-.13 0-.35.03-.66.08zm80.87-23.45c-2.72 9.8-14.93 9.86-26.72 3.3-10.17-5.64-13.8-17.98-5-22.87a66.53 66.53 0 0 0 4.48-2.7l2.03-1.3a50.15 50.15 0 0 1 3.92-2.3c4.73-2.43 8.82-2.8 14-.72 9.16 3.66 10.98 13.33 7.3 26.6zm-20.83-24.98a49.26 49.26 0 0 0-3.84 2.25l-2.03 1.3c-.84.53-1.5.95-2.16 1.35-.82.5-1.6.96-2.38 1.39-7.94 4.4-4.59 15.8 5 21.12 11.31 6.29 22.8 6.23 25.28-2.7 3.57-12.83 1.85-21.97-6.7-25.4-4.9-1.95-8.69-1.62-13.17.7zm17.85 12.15c0 5.7-2.44 9-6.64 9.96-3.3.76-7.56-.05-11.08-1.81l-1.89-.94c-.67-.34-1.18-.62-1.63-.88-4.07-2.38-4.13-4.97.34-10.93 6.8-9.06 20.9-7.16 20.9 4.6zm-1 0c0-5.3-2.87-8.55-7.32-9.16-4.23-.57-8.99 1.44-11.78 5.16-4.15 5.54-4.1 7.44-.64 9.47.44.25.93.51 1.59.85l1.87.93c3.34 1.67 7.36 2.44 10.42 1.74 3.73-.86 5.86-3.74 5.86-9zm196.5 281c0-12.8 2.44-16.74 18.48-29.77a56.8 56.8 0 0 1 7.61-5.2c2.6-1.5 5.33-2.82 8.5-4.18 1.24-.53 2.48-1.05 4.1-1.7l3.92-1.57c9.4-3.83 13.74-6.7 16.62-12.05 1.2-2.22 2.21-4.4 3.23-6.83a148.57 148.57 0 0 0 1.54-3.84l.3-.74.56-1.44c3.2-8.02 6.05-12.08 12.7-16.5a35.26 35.26 0 0 0 4.96-4 46.36 46.36 0 0 0 3.88-4.29c.27-.34 2.55-3.2 3.2-3.98 3.48-4.15 6.51-5.9 11.51-5.9 3.08 0 5.62-.63 9.57-2.1 5.42-2.02 6.53-2.34 8.96-2.2 2.53.13 4.85 1.26 7.18 3.59 1.3 1.3 5.55 5.83 6.52 6.78 5.06 5 9.44 6.92 17.77 6.92a197.5 197.5 0 0 1 12.08.45c15.93.87 21.94.57 25.28-2.21 6.91-5.77 11.64-2.73 11.64 7.76 0 10.73-8.6 20-19 20-4.8 0-8.32 1.43-9.34 3.67-1.12 2.48.68 6.15 5.98 10.57 13.6 11.33 11.24 20.76-7.64 20.76a21.91 21.91 0 0 0-14.6 5.24c-3.28 2.71-5.8 5.86-9.85 11.82l-1.52 2.25c-3.1 4.57-5.01 7.1-7.32 9.4-6.21 6.21-9.3 7.64-13.05 6.89l-1-.23a10.82 10.82 0 0 0-2.66-.37c-1.6 0-2.41.67-8.18 6.22-4.85 4.67-8.07 6.78-11.82 6.78-1.33 0-3.46 1.15-6.45 3.45-1.27.98-2.68 2.14-4.5 3.7l-4.92 4.29a181.11 181.11 0 0 1-4.54 3.82c-9.33 7.56-15.63 10.2-20.21 6.52-2.7-2.15-4.14-4.51-4.63-7.26-.37-2.04-.26-3.63.29-7.3.87-5.85.65-8.42-1.83-11.6-2.32-2.98-2.96-3.22-3.77-2.39-.25.26-1.35 1.63-1.61 1.94-2.21 2.5-4.85 3.57-9 2.82-4.6-.84-5.57-4.11-4.72-10.09l.24-1.56c.6-3.66.68-4.93.25-5.8-.44-.86-1.9-.94-5.23.4l-.74.29c-13.78 5.54-15.26 6.09-19.43 6.67-6.03.84-9.31-1.6-9.31-7.9zm2 0c0 5 2.14 6.6 7.04 5.92 3.91-.55 5.43-1.1 18.95-6.55l.75-.3c4.17-1.66 6.7-1.54 7.76.58.71 1.43.62 2.76-.06 7l-.24 1.53c-.72 5.04-.06 7.27 3.09 7.84 3.43.62 5.38-.17 7.15-2.18.2-.23 1.34-1.66 1.68-2 1.9-1.96 3.82-1.25 6.78 2.55 2.9 3.74 3.17 6.77 2.22 13.12-1 6.75-.52 9.4 3.62 12.71 3.49 2.8 9.1.45 17.7-6.51 1.35-1.1 2.75-2.28 4.49-3.78l4.93-4.3c1.84-1.58 3.27-2.76 4.58-3.77 3.34-2.56 5.74-3.86 7.67-3.86 3.04 0 5.95-1.9 10.43-6.22l2.46-2.39c.94-.89 1.67-1.56 2.37-2.13 1.81-1.49 3.3-2.26 4.74-2.26 1.03 0 1.81.13 3.1.42.7.16.71.17.96.21 2.96.6 5.45-.55 11.23-6.33 2.2-2.2 4.06-4.65 7.09-9.11l1.52-2.25c4.15-6.11 6.76-9.37 10.22-12.24a23.9 23.9 0 0 1 15.88-5.7c16.87 0 18.62-7.01 6.36-17.23-5.9-4.92-8.12-9.41-6.52-12.93 1.42-3.12 5.67-4.84 11.16-4.84 9.25 0 17-8.34 17-18 0-8.94-2.88-10.79-8.36-6.23-3.94 3.28-9.98 3.59-26.67 2.68l-1.02-.06c-5.09-.27-7.99-.39-10.95-.39-8.88 0-13.76-2.14-19.18-7.5-1-.98-5.26-5.53-6.53-6.79-1.99-1.99-3.86-2.9-5.87-3-2.03-.12-3.06.18-8.15 2.07-4.15 1.55-6.9 2.22-10.27 2.22-4.33 0-6.84 1.46-9.98 5.2-.63.74-2.89 3.6-3.18 3.95a48.29 48.29 0 0 1-4.04 4.46 37.26 37.26 0 0 1-5.24 4.23c-6.26 4.17-8.9 7.91-11.95 15.58l-.57 1.43-.28.74a531.5 531.5 0 0 1-1.56 3.88 77.49 77.49 0 0 1-3.32 7c-3.16 5.88-7.82 8.97-17.63 12.96l-3.92 1.58c-1.6.64-2.84 1.15-4.05 1.67a79.2 79.2 0 0 0-8.3 4.08 54.8 54.8 0 0 0-7.35 5.02c-15.62 12.7-17.74 16.13-17.74 28.23zm133.22-79.76c3.06 1.53 6.54 2.02 10.68 1.7 2.53-.2 4.91-.62 8.8-1.49 5.36-1.19 6.33-1.38 8.33-1.54 2.78-.23 4.82.17 6.29 1.4 1.58 1.31 1.96 2.72 1.26 4.22-.66 1.38-1.05 1.74-5.05 5.07-3.53 2.93-5.03 4.83-5.03 7.09 0 7.3 1.29 10.02 7.83 15.62 3.86 3.3 5.93 6.84 5.28 9.62-.75 3.25-4.96 5.02-12.61 5.02-7.18 0-12.7 4.61-20.03 14.68-.5.7-3.96 5.57-4.94 6.87a38.89 38.89 0 0 1-4.72 5.5c-1.06.98-2.09 1.7-3.1 2.15-2.85 1.26-5.05 1.57-9.83 1.74-7.66.27-10.87 1.45-14.98 7.1-1.58 2.17-3.11 4-4.68 5.6a42.87 42.87 0 0 1-8.65 6.69c-.15.08-10.69 6.19-14.8 8.83-3.76 2.42-6.45 2.04-8.22-.77-1.28-2.03-1.9-4.54-2.87-10.35-.84-5.08-1.27-7.08-2.06-8.93-.97-2.3-2.21-3.24-4.02-2.88-6.2 1.24-8.95 1.39-10.98.2-2.37-1.4-3.13-4.62-2.62-10.73.16-1.96-1.04-2.87-3.76-3.04-2.24-.13-4.9.2-9.94 1.12l-.69.12c-7.97 1.45-10.72 1.72-12.72.73-2.91-1.43-1.6-5.27 4.23-12.21 5.48-6.53 10.6-10.81 15.76-13.53 3.74-1.97 5.94-2.65 12.16-4.1 7.29-1.72 10.4-3.51 14.04-9.31 2.96-4.75 10.74-18.62 12.14-20.84 3.59-5.67 6.8-9.1 11.05-11.34 2.6-1.38 4.72-2.82 9.17-6.07l1.38-1.01c7.85-5.72 12.3-7.98 17.68-7.98 4.22 0 6.49 1.36 9.13 4.77.34.43 1.67 2.22 2 2.67.85 1.09 1.6 1.98 2.45 2.83a24.29 24.29 0 0 0 6.64 4.78zm-.44.9c-2.8-1.4-5-3.03-6.92-4.97-.87-.9-1.65-1.81-2.51-2.93-.35-.46-1.68-2.25-2.01-2.67-2.47-3.18-4.46-4.38-8.34-4.38-5.09 0-9.4 2.2-17.09 7.78l-1.38 1.01c-4.49 3.29-6.63 4.74-9.3 6.15-4.06 2.15-7.16 5.45-10.66 11-1.39 2.19-9.16 16.05-12.15 20.82-3.79 6.07-7.13 7.98-14.66 9.75-6.13 1.45-8.27 2.1-11.92 4.02-5.04 2.66-10.05 6.86-15.46 13.3-5.43 6.46-6.53 9.69-4.55 10.66 1.7.84 4.48.57 12.1-.81l.7-.13c5.12-.93 7.82-1.27 10.17-1.12 3.21.2 4.92 1.48 4.7 4.11-.48 5.76.2 8.64 2.13 9.78 1.73 1.02 4.34.88 10.27-.31 2.35-.47 4 .78 5.14 3.47.83 1.95 1.27 4 2.07 8.8l.06.36c.94 5.65 1.55 8.11 2.72 9.98 1.46 2.3 3.52 2.6 6.84.46 4.14-2.66 14.69-8.77 14.81-8.85a41.9 41.9 0 0 0 8.46-6.54 47.89 47.89 0 0 0 4.6-5.48c4.32-5.95 7.81-7.23 15.74-7.5 4.66-.17 6.76-.47 9.46-1.67.9-.4 1.85-1.06 2.84-1.96a38.03 38.03 0 0 0 4.6-5.36c.96-1.3 4.4-6.16 4.93-6.87 7.5-10.31 13.22-15.09 20.83-15.09 7.24 0 11.02-1.6 11.64-4.24.54-2.32-1.36-5.55-4.97-8.64-6.75-5.79-8.17-8.79-8.17-16.38 0-2.67 1.64-4.74 5.39-7.86 3.8-3.17 4.23-3.56 4.78-4.73.5-1.06.25-1.99-.99-3.03-2.23-1.85-4.72-1.65-13.76.36-3.93.87-6.35 1.3-8.94 1.5-4.3.34-7.97-.18-11.2-1.8zm-28-3.9c5.65-2.82 8.96-2.2 12.9 1.37.56.5 2.6 2.47 3.02 2.87 4.2 3.89 8.07 5.71 14.3 5.71 11.37 0 14 1.41 16.1 8.09.26.83 1.35 4.6 1.66 5.62.8 2.63 1.64 5.03 2.7 7.6 2.13 5.17 2.64 8.32 1.72 10.24-.77 1.61-2.1 2.18-5.37 2.79-2.32.43-2.8.53-3.85.85-1.85.58-3.35 1.4-4.6 2.66-1 1-2.02 2.13-3.31 3.66-.6.71-2.91 3.5-3.46 4.14-7.2 8.54-12.43 12.35-19.59 12.35-3.76 0-6.95 1.28-10.59 4-1.84 1.37-11.62 10.31-15.22 13.06a73.09 73.09 0 0 1-8.95 5.88c-4.58 2.54-7.35 3.22-8.98 2.23-1.32-.8-1.65-2.07-1.94-5.5a52.53 52.53 0 0 0-.16-1.81c-.54-4.73-2.24-6.86-7.16-6.86-7.11 0-8.85-1.23-9.73-5.41-.96-4.61-2.1-6.7-6.55-9.67-3.97-2.65-4.31-5.42-1.52-8.22 2-2 4.63-3.5 11.35-6.87 6.61-3.3 9.2-4.8 11.1-6.68a39.09 39.09 0 0 0 5.3-6.48c.98-1.5 1.83-3.04 2.88-5.13l2.12-4.3c.91-1.83 1.72-3.37 2.61-4.98 5.74-10.32 10.37-14.78 23.22-21.2zm-22.34 21.7c-.89 1.59-1.69 3.12-2.6 4.94l-2.11 4.3a52.9 52.9 0 0 1-2.94 5.23 40.08 40.08 0 0 1-5.44 6.63c-2 2-4.62 3.51-11.35 6.87-6.6 3.3-9.2 4.8-11.1 6.69-2.33 2.34-2.08 4.37 1.38 6.67 4.7 3.14 5.96 5.46 6.97 10.3.78 3.7 2.09 4.62 8.75 4.62 5.5 0 7.57 2.57 8.15 7.75.06.5.09.82.17 1.84.25 3.06.55 4.17 1.46 4.72 1.2.74 3.69.13 7.98-2.25a72.09 72.09 0 0 0 8.82-5.8c3.55-2.7 13.34-11.65 15.24-13.07 3.79-2.83 7.18-4.19 11.18-4.19 6.77 0 11.8-3.67 18.83-12l3.45-4.13a60.07 60.07 0 0 1 3.37-3.72 11.72 11.72 0 0 1 5.01-2.91c1.1-.34 1.6-.45 3.97-.89 2.95-.55 4.07-1.02 4.65-2.23.76-1.59.28-4.5-1.74-9.43a84.46 84.46 0 0 1-2.74-7.69c-.31-1.03-1.4-4.8-1.66-5.61-1.95-6.2-4.16-7.39-15.14-7.39-6.5 0-10.61-1.93-14.98-5.98-.44-.4-2.46-2.37-3.01-2.86-3.65-3.3-6.52-3.85-11.79-1.21-12.67 6.33-17.15 10.65-22.78 20.8zm55.86 11.93c-2.98 6.45-16.78 15.26-26.74 15.26-5.33 0-7.56-2.98-7.11-7.86.32-3.48 2.1-7.91 3.93-10.61l1.52-2.32a44.95 44.95 0 0 1 1.88-2.7c3.66-4.8 7.85-7.45 13.62-7.45 9.06 0 15.75 9.52 12.9 15.68zm-.9-.42c2.52-5.47-3.65-14.26-12-14.26-5.4 0-9.33 2.48-12.82 7.06-.6.8-1.17 1.6-1.85 2.64 0 0-1.2 1.87-1.52 2.33-1.74 2.57-3.46 6.85-3.77 10.14-.4 4.33 1.43 6.77 6.12 6.77 9.57 0 23.02-8.58 25.83-14.68zm-69.67 20.74c2.08.18 4.44.81 5.88 1.8 2.12 1.47 2.2 3.6-.26 6.05-5.14 5.15-12.85 4.34-12.85-1.35 0-4.66 3.14-6.84 7.23-6.5zm-.09 1c-3.56-.3-6.14 1.5-6.14 5.5 0 4.58 6.53 5.26 11.15.65 2.03-2.04 1.98-3.43.4-4.52-1.27-.88-3.48-1.47-5.4-1.63zm29.59-225.95c4.64 2.35 17.27 8.24 19.39 9.43a24.14 24.14 0 0 1 7.05 5.64 45.03 45.03 0 0 1 3.75 5.2c2.4 3.78.04 7.66-6.2 11.63-4.97 3.16-12.18 6.3-21.95 9.82-4.84 1.74-19.63 6.68-21.1 7.2-6.59 2.33-14.85.1-25.14-5.86-3.93-2.27-8-5-12.94-8.54-2.23-1.61-9.5-6.99-10.7-7.85a81.21 81.21 0 0 0-8.63-5.7c-4.82-2.6-4.45-6.64.17-12.13 3.27-3.88 4.17-4.67 18.1-16.33a230.2 230.2 0 0 0 8.89-7.74 95.2 95.2 0 0 0 4.72-4.66c5.08-5.43 9.8-6.49 14.97-3.92 2.24 1.1 4.53 2.85 7.43 5.52 1.48 1.37 6.94 6.72 7.98 7.7 5.2 4.91 9.46 8.2 14.2 10.6zm-.46.9c-4.85-2.45-9.18-5.79-14.44-10.76-1.05-1-6.5-6.34-7.97-7.69-2.83-2.61-5.06-4.3-7.2-5.37-4.75-2.36-9-1.4-13.8 3.71a96.18 96.18 0 0 1-4.76 4.71c-2.48 2.3-5.16 4.62-8.92 7.77-13.86 11.6-14.77 12.4-17.98 16.21-4.28 5.08-4.58 8.4-.46 10.61 2.23 1.2 4.9 2.99 8.74 5.77 1.2.87 8.47 6.24 10.7 7.85a154.8 154.8 0 0 0 12.85 8.49c10.06 5.82 18.07 7.98 24.3 5.78 1.48-.52 16.27-5.47 21.1-7.2 9.7-3.5 16.86-6.61 21.75-9.72 5.84-3.71 7.9-7.1 5.9-10.26a44.09 44.09 0 0 0-3.67-5.08 23.16 23.16 0 0 0-6.78-5.42c-2.08-1.16-14.68-7.05-19.36-9.4zm-38.83 8.05c3.11-.37 5.7-.13 8.4.7 2.15.66 2.74.93 8.64 3.77 4.75 2.29 8.39 3.86 13.19 5.56 8.38 2.97 11.32 6.23 8.83 9.76-2.08 2.94-8.04 5.92-17.84 9.18-8.45 2.82-15.48 2.35-21.43-.9-4.65-2.55-8.33-6.5-12.15-12.3-2.9-4.41-2.73-8.2.16-11.06 2.48-2.45 6.87-4.07 12.2-4.7zm.12 1c-5.13.6-9.33 2.16-11.62 4.42-2.53 2.5-2.68 5.77-.02 9.8 3.73 5.68 7.3 9.51 11.8 11.97 5.7 3.11 12.43 3.57 20.62.84 9.59-3.2 15.44-6.12 17.34-8.82 1.94-2.75-.5-5.45-8.35-8.24-4.84-1.72-8.5-3.3-13.28-5.6-5.84-2.81-6.42-3.07-8.5-3.71a18.42 18.42 0 0 0-8-.66zM202.5 500.38c0 4.78-1.45 7.56-4.43 8.93-2.29 1.05-4.55 1.23-10.79 1.2l-1.78-.01c-9.19 0-17-7.65-17-15.5 0-7.59 10.6-10.51 19.74-5.44 2.78 1.55 4.21 1.94 8.57 2.75 4.44.83 5.69 2.27 5.69 8.07zm-1 0c0-5.3-.9-6.34-4.88-7.08-4.45-.83-5.96-1.25-8.86-2.86-8.57-4.76-18.26-2.1-18.26 4.56 0 7.3 7.36 14.5 16 14.5h1.79c6.06.04 8.26-.14 10.36-1.1 2.6-1.2 3.85-3.6 3.85-8.02zm33.33-117.85c3.71-1.31 8.7-2.7 16.1-4.55 2.58-.65 16.53-4.04 20.56-5.05 19.59-4.93 31.55-8.9 38.23-13.35 14.93-9.95 36.87-33.88 43.83-47.8 2.25-4.5 4.65-6.38 7.68-6.25 1.26.06 2.61.45 4.32 1.2a50.81 50.81 0 0 1 3.54 1.7l1.26.63c4.78 2.34 8.38 3.44 12.65 3.44 7.2 0 10.01 3.07 8.35 7.91-1.4 4.06-5.92 8.91-11.1 12.02-8.3 4.98-11.75 17.3-11.75 33.57 0 3.59-1.37 6.28-3.98 8.36-1.98 1.58-4.2 2.6-8.47 4.16l-1.02.37c-4.85 1.75-6.98 2.77-8.68 4.46-5.09 5.1-12.54 7.15-20.35 7.15-1.38 0-2.47.92-3.99 3.1-.29.41-1.32 1.95-1.47 2.18-2.68 3.92-4.93 5.72-8.54 5.72-7.84 0-10.74.93-21.76 6.94-5.18 2.82-8.8 3.58-14.66 3.68-.26 0-.47 0-.92.02-4.82.06-7.12.3-10.51 1.34a73.43 73.43 0 0 0-8.89 3.56c-2.17 1-10.53 5.01-10.23 4.87-7.79 3.7-13.32 5.98-18.9 7.57-12.41 3.55-18.58 2.24-27.42-4.07-2.58-1.85-2.72-4.43-.83-7.62 1.45-2.45 3.9-5.09 8.08-8.97l1.78-1.64c3.92-3.6 4.48-4.11 5.9-5.53 2.32-2.32 3.12-3.5 5.48-7.63 1.93-3.36 3.37-5.11 6.27-7.06 2.3-1.54 5.34-2.98 9.44-4.43zm.34.94c-4.03 1.42-7 2.83-9.22 4.32-2.75 1.85-4.1 3.49-5.96 6.73-2.4 4.2-3.24 5.44-5.64 7.83-1.43 1.44-2 1.96-5.94 5.57l-1.77 1.63c-4.1 3.82-6.52 6.41-7.9 8.75-1.65 2.79-1.54 4.8.55 6.3 8.6 6.14 14.46 7.38 26.57 3.92 5.5-1.57 11-3.84 18.74-7.51-.3.14 8.06-3.88 10.24-4.88a74.3 74.3 0 0 1 9.01-3.6c3.51-1.09 5.89-1.33 10.8-1.4h.91c5.72-.1 9.18-.83 14.2-3.57 11.16-6.08 14.2-7.06 22.24-7.06 3.19 0 5.2-1.6 7.71-5.28l1.48-2.2c1.7-2.43 3-3.52 4.81-3.52 7.57 0 14.78-2 19.65-6.85 1.83-1.84 4.04-2.9 9.04-4.7l1.02-.37c8.6-3.13 11.79-5.67 11.79-11.58 0-16.6 3.53-29.2 12.24-34.43 5-3 9.35-7.67 10.66-11.48 1.42-4.13-.83-6.59-7.4-6.59-4.45 0-8.19-1.14-13.09-3.54-7.52-3.67-6.78-3.34-8.72-3.43-2.58-.1-4.65 1.52-6.74 5.7-7.04 14.07-29.1 38.14-44.17 48.19-6.81 4.54-18.84 8.52-38.55 13.48-4.03 1.02-17.98 4.4-20.56 5.05-7.37 1.84-12.33 3.23-16 4.52zM252 387.5c2.08 0 4-.2 7.25-.69 5.22-.77 6.64-.9 8.46-.5 2.52.56 3.79 2.35 3.79 5.69 0 4.05-2.27 7.29-6.62 10.11-3.24 2.1-6.53 3.53-14.15 6.4l-.27.1-2.28.86c-3.04 1.16-5.27 2.52-9.33 5.43l-.8.57c-8.19 5.88-13.35 8.03-23.05 8.03-4.98 0-6.88-2.03-5.75-5.62.87-2.81 3.58-6.56 7.8-11.13 1.26-1.37 2.64-2.8 4.15-4.3 3.17-3.14 11.25-10.61 11.45-10.8.46-.47.93-.89 1.4-1.26 3.38-2.71 5.77-3.08 14.18-2.93 1.65.03 2.63.04 3.77.04zm0 1c-1.15 0-2.13-.01-3.79-.04-8.18-.14-10.4.2-13.54 2.71-.44.35-.88.74-1.32 1.18-.2.21-8.3 7.69-11.45 10.82a134.6 134.6 0 0 0-4.12 4.26c-4.12 4.47-6.76 8.12-7.58 10.75-.9 2.88.45 4.32 4.8 4.32 9.46 0 14.44-2.07 22.46-7.84l.8-.57c4.13-2.96 6.42-4.36 9.56-5.56l2.3-.86.25-.1c7.55-2.84 10.8-4.25 13.97-6.3 4.08-2.65 6.16-5.6 6.16-9.27 0-2.89-.97-4.26-3-4.7-1.65-.37-3.05-.25-8.1.5-3.3.5-5.26.7-7.4.7zm112.47-45.34c-1.88 5.44-1.98 6.76-.98 12.76 1.18 7.06-1.38 16.58-5.49 16.58a16.89 16.89 0 0 0-1.51.07l-.64.04c-2.86.18-4.83.17-6.94-.17-6.55-1.06-10.41-5.14-10.41-13.44 0-13.9 2.14-19.69 8.13-26.33a21.9 21.9 0 0 0 2.52-3.75c.59-1.03 2.78-5.13 2.72-5.01 4.44-8.14 7.71-11.53 12.25-10.4 1.17.3 2.2.77 3.58 1.59l1.39.84a20 20 0 0 0 3.1 1.6c.7.27 1.8.32 4.75.26l.72-.01c3.16-.05 4.78.08 5.83.66 1.61.89 1.2 2.56-1.14 4.9a215.9 215.9 0 0 1-3.86 3.76c-10.6 10.1-12.75 12.4-14.02 16.05zm-.94-.32c1.34-3.9 3.46-6.17 14.27-16.46 1.55-1.47 2.73-2.62 3.85-3.73 1.94-1.95 2.17-2.88 1.35-3.33-.82-.45-2.37-.58-5.32-.53l-.72.01c-3.14.06-4.26.02-5.14-.34-1.06-.41-1.97-.9-3.25-1.67l-1.38-.83a12.1 12.1 0 0 0-3.31-1.47c-3.88-.97-6.92 2.17-11.13 9.9.07-.13-2.14 3.98-2.73 5.02a22.71 22.71 0 0 1-2.65 3.92c-5.81 6.47-7.87 12-7.87 25.67 0 7.79 3.48 11.47 9.57 12.45 2.01.33 3.92.34 6.71.16a371.33 371.33 0 0 0 1.23-.07c.42-.03.73-.04.99-.04 3.2 0 5.6-8.9 4.5-15.42-1.02-6.16-.91-7.64 1.03-13.24zm-9.26 12.42c.58.52 2.5 1.9 2.55 1.93 1.96 1.57 2.04 3.31.01 6.36-3.74 5.64-8.83 3.09-8.83-4.55 0-3.81.51-5.67 2.07-6.02 1.18-.26 2 .3 4.2 2.28zm-1.34 1.48c-1.5-1.35-2.23-1.85-2.43-1.8-.17.03-.5 1.23-.5 4.06 0 5.87 2.67 7.21 5.17 3.45 1.5-2.26 1.47-2.84.4-3.7.03.03-1.95-1.4-2.64-2zm222.9-130.19c2.2-1.1 3.67-1.66 5.88-2.36l.28-.09a48.92 48.92 0 0 0 8.79-3.55c4.17-2.08 6.35-1.88 6.96.84.44 2 .2 4.01-1.25 12.7-2.27 13.62-9.16 26.14-21.17 36.3-4.3 3.63-7.41 4.39-9.75 2.44-1.88-1.57-3.1-4.57-4.61-10.48-.3-1.15-1.43-5.83-1.72-6.96a114.18 114.18 0 0 0-2.71-9.22c-2.4-6.82-3.03-10.78-2.1-12.94.77-1.83 2.08-2.24 5.6-2.45 1.49-.09 2.09-.14 2.97-.28l1.95-.33c.72-.12 1.22-.2 1.68-.29 1.1-.2 1.92-.38 2.71-.6 1.7-.49 3.42-1.2 6.49-2.73zm.44.9c-3.11 1.54-4.88 2.29-6.65 2.79-.84.23-1.69.42-2.81.63a108.77 108.77 0 0 1-3.81.63c-.77.13-1.39.19-2.92.28-3.13.18-4.17.51-4.74 1.85-.78 1.84-.2 5.62 2.13 12.2a115.12 115.12 0 0 1 2.74 9.31l1.72 6.96c1.46 5.7 2.62 8.58 4.28 9.96 1.87 1.56 4.49.93 8.47-2.44 11.82-10 18.6-22.3 20.83-35.7 1.4-8.45 1.65-10.51 1.25-12.31-.41-1.87-1.86-2-5.54-.16a49.87 49.87 0 0 1-8.93 3.6l-.28.1a35.4 35.4 0 0 0-5.74 2.3zm-4.5 6.58c1.37-.32 2.5-.75 3.9-1.42.35-.18 2.57-1.31 3.32-1.67 1.5-.71 2.97-1.31 4.7-1.89 2.7-.9 4.64-.77 5.88.4.98.94 1.34 2.26 1.41 4.18.02.4.02.7.02 1.37 0 5.63-4.63 16.88-11.34 22.75-4.34 3.8-7.31 4.67-9.92 2.52-2.06-1.7-3.5-4.65-6.67-12.91-1.86-4.83-2.05-8.1-.68-10.2 1.12-1.7 2.9-2.36 5.83-2.7l1.26-.12c1.19-.12 1.75-.19 2.3-.31zm-2.1 2.3-1.22.12c-2.4.27-3.7.76-4.39 1.81-.93 1.43-.78 4.1.87 8.38 3.02 7.84 4.41 10.71 6.08 12.09 1.63 1.34 3.64.75 7.33-2.48C584.6 250.77 589 240.08 589 235c0-.64 0-.93-.02-1.29-.05-1.44-.3-2.33-.79-2.8-.6-.57-1.8-.65-3.87.04a37.95 37.95 0 0 0-4.47 1.8c-.72.34-2.93 1.47-3.32 1.66a19.54 19.54 0 0 1-4.3 1.56c-.66.16-1.28.24-2.56.36zm-227.73-88.98c-1.59 4.3-3.54 7.25-7.14 11.4l-2.6 2.97a67.02 67.02 0 0 0-2.63 3.23 46.4 46.4 0 0 0-4.68 7.5c-2.85 5.7-7.14 10.18-12.85 13.89-4.25 2.76-8.25 4.62-15.67 7.59-11.01 4.4-16.43 1.26-27.22-16.4-2.86-4.69-8.8-8.63-17.98-12.66-3-1.33-12.88-5.24-14.43-5.92-4.96-2.18-7.04-3.72-6.42-5.85.67-2.32 5.3-4.05 15.48-6.08 16.63-3.32 26.93-3.82 39.93-3.02 7.9.49 9.67.5 12.74-.26 1.99-.48 3.92-1.3 6-2.6l2.79-1.71c9.86-6.14 12.94-7.96 17.3-9.9 6.03-2.71 10.57-3.32 13.94-1.4 7.2 4.12 7.68 7.7 3.44 19.22zm-1.88-.7c3.95-10.7 3.6-13.26-2.56-16.78-2.66-1.52-6.62-.99-12.12 1.48-4.24 1.9-7.3 3.7-17.07 9.77l-2.79 1.73a22.6 22.6 0 0 1-6.57 2.84c-3.36.81-5.22.8-13.34.3-12.84-.78-22.97-.29-39.41 3-4.9.97-8.45 1.88-10.79 2.75-2.03.76-3.04 1.45-3.17 1.91-.16.57 1.48 1.79 5.3 3.46 1.5.67 11.39 4.58 14.44 5.93 9.52 4.19 15.74 8.3 18.87 13.44 10.35 16.93 14.87 19.56 24.78 15.6 7.3-2.93 11.21-4.75 15.33-7.42 5.42-3.53 9.47-7.75 12.15-13.1 1.44-2.9 3.02-5.4 4.86-7.82a68.95 68.95 0 0 1 2.72-3.33l2.6-2.97c3.46-3.99 5.28-6.75 6.77-10.79zm-6.64-.39c-7.94 12.8-18.53 21.75-33.3 25.23-7.82 1.83-12.47-.79-13.12-5.93-.55-4.45 2.29-9.06 6-9.06 3.02 0 5.6-1.68 15.38-9.16 1.47-1.12 2.57-1.96 3.66-2.74 4.4-3.2 7.77-5.17 10.82-6.08 5.57-1.67 9.33-2.15 11.35-1.22 2.5 1.14 2.22 4.13-.79 8.96zm-.84-.52c2.72-4.4 2.94-6.74 1.21-7.53-1.71-.79-5.32-.33-10.65 1.27-2.9.87-6.2 2.79-10.51 5.92-1.08.79-2.18 1.62-3.65 2.74-10.08 7.72-12.62 9.36-15.98 9.36-3.02 0-5.5 4.02-5 7.94.56 4.5 4.62 6.78 11.89 5.07 14.48-3.4 24.86-12.18 32.69-24.77zM461.17 33.53c13.88 4.96 20.75 4.96 31.62.01 3.02-1.37 5.47-2.94 11-6.82 5.57-3.92 8.05-5.51 11.14-6.92 4.14-1.88 7.78-2.38 11.22-1.28 3.92 1.26 6.2 12.3 6.78 28.45.5 14.2-.52 28.93-2.46 34.2-1.82 4.93-5.86 8.17-11.51 10.02A41.7 41.7 0 0 1 506 93.01c-5.79 0-9 2.4-12.2 7.64-.37.59-1.55 2.6-1.71 2.87-1.75 2.9-3.05 4.33-4.93 4.95-.94.32-2.07.83-3.87 1.74l-2.43 1.23c-1.03.53-1.87.94-2.7 1.34-6.43 3.1-11.73 4.72-17.16 4.72-5.71 0-10.04 2.09-14.02 5.92-1.16 1.11-4.2 4.53-4.63 4.94-2.54 2.44-5.93 4.24-10.85 6.1-1.4.52-5.98 2.13-6.25 2.22l-2.06.78c-.89.36-1.78.63-2.7.81-5.55 1.14-11.14-.54-17.98-4.42-1.27-.73-5.13-3.06-5.76-3.42-2.05-1.16-4.12-1.53-9.09-1.9l-1.73-.15c-4.78-.4-7.68-1.14-10.22-2.97-5-3.61-6.77-7.76-5.65-12.33 1.33-5.42 6.5-11.02 14.85-17.28a169.2 169.2 0 0 1 6.5-4.61c-.33.23 4.33-2.92 5.3-3.6 2.73-1.91 4.8-3.9 12.75-12.04l1.09-1.1c3.49-3.56 5.89-5.89 8.12-7.83 2.9-2.5 4.72-5.95 7.5-13.05l.63-1.61c2.7-6.92 4.28-10 6.87-12.33 1.42-1.28 6.68-6.54 7.93-7.5 3.98-3 8.01-2.73 19.57 1.4zm-.34.94c-11.26-4.02-15-4.28-18.62-1.53-1.19.9-6.4 6.11-7.88 7.43-2.42 2.18-3.96 5.19-6.6 11.95l-.63 1.61c-2.83 7.26-4.72 10.8-7.77 13.45a141.85 141.85 0 0 0-9.16 8.87c-8.02 8.2-10.08 10.2-12.88 12.16-.99.69-5.65 3.84-5.31 3.6-2.5 1.71-4.52 3.13-6.47 4.59-8.17 6.13-13.23 11.6-14.48 16.72-1.02 4.15.58 7.9 5.26 11.27 2.36 1.7 5.11 2.4 9.72 2.8l1.73.13c5.12.4 7.28.78 9.5 2.05.65.36 4.5 2.7 5.76 3.4 6.66 3.78 12.04 5.4 17.29 4.32.86-.17 1.7-.42 2.52-.75a67 67 0 0 1 2.1-.8c.28-.1 4.86-1.7 6.24-2.22 4.8-1.8 8.08-3.56 10.5-5.88.4-.38 3.44-3.8 4.63-4.94 4.16-4 8.72-6.2 14.72-6.2 5.25 0 10.42-1.59 16.73-4.62.82-.4 1.65-.8 2.68-1.33.12-.06 1.93-.99 2.43-1.23 1.84-.93 3-1.46 4-1.8 1.6-.52 2.76-1.82 4.39-4.52l1.7-2.88c3.39-5.5 6.87-8.11 13.07-8.11 4.45 0 8.73-.49 12.64-1.77 5.4-1.76 9.2-4.8 10.9-9.41 1.87-5.11 2.9-19.75 2.39-33.83-.56-15.53-2.81-26.48-6.08-27.52-3.18-1.02-6.57-.55-10.5 1.23-3.02 1.37-5.47 2.94-11 6.83-5.57 3.92-8.05 5.5-11.14 6.92-11.13 5.05-18.26 5.05-32.38.01zM475 55c5.38 0 7.55-.21 9.72-.96 1.26-.43 9.95-4.8 14.88-6.96 1.9-.82 3.56-2.44 6.6-6.04 2.56-3.04 3.19-3.75 4.4-4.84 3.7-3.35 7.07-3.28 10.22 1.23 6.23 8.9 5.61 15.94.07 27.02a71.26 71.26 0 0 0-2.5 5.48c-.32.8-1 2.7-1.09 2.9-.17.45-.34.81-.54 1.17-.63 1.14-1.56 2.21-4.05 4.7-2.4 2.4-5.16 3.27-11.68 4.33-1.81.3-2.2.36-3 .51-6.02 1.1-9.6 2.69-12.24 6.07-3.57 4.59-7.9 7.48-14.98 10.74-.55.24-1.1.5-1.8.8l-1.78.8a60.08 60.08 0 0 0-7.7 3.9c-2.57 1.6-4.79 2.35-9.42 3.46-8.58 2.06-12.28 3.76-17.37 9.36-5.12 5.64-10.17 7.64-16.63 6.7-5.36-.79-10.63-3.01-23.56-9.48-6.3-3.15-6.43-7.78-1.5-13.56 3.38-3.94 3.52-4.06 19.4-16.44 8.12-6.33 12.97-10.57 16.63-14.88 2.53-2.98 4.2-5.73 4.96-8.3 5.5-18.3 12.5-21.98 22.78-15.56 1.95 1.22 6.61 4.55 7.18 4.9 3.36 2.15 6.52 2.95 13 2.95zm0 2c-6.84 0-10.37-.89-14.08-3.26-.63-.4-5.27-3.71-7.16-4.9-9.05-5.65-14.66-2.7-19.8 14.45-.86 2.87-2.67 5.85-5.35 9.01-3.78 4.45-8.7 8.75-16.94 15.17-15.66 12.21-15.86 12.38-19.1 16.16-4.17 4.9-4.09 8 .88 10.48 12.71 6.35 17.89 8.54 22.94 9.28 5.78.84 10.18-.9 14.87-6.06 5.42-5.96 9.45-7.82 18.38-9.96 4.43-1.07 6.5-1.76 8.83-3.22a61.7 61.7 0 0 1 7.94-4.02l1.78-.8 1.78-.8c6.82-3.13 10.91-5.87 14.24-10.14 3-3.87 7-5.64 13.46-6.82.83-.15 1.21-.21 3.04-.51 6.1-1 8.6-1.78 10.58-3.77 2.36-2.36 3.21-3.34 3.72-4.26.15-.27.29-.56.44-.94.06-.15.75-2.06 1.09-2.9.64-1.6 1.45-3.4 2.57-5.64 5.24-10.49 5.8-16.8.07-24.98-2.4-3.44-4.37-3.48-7.24-.89-1.11 1-1.73 1.7-4.22 4.65-3.24 3.85-5.04 5.59-7.32 6.59-4.82 2.1-13.62 6.53-15.03 7.01-2.44.84-4.79 1.07-10.37 1.07zm-12.7 8.6c5.47 3.9 10.34 3.72 18.23.88 5.39-1.94 5.92-2.1 7.7-2.1 2.5-.01 4.21 1.36 5.24 4.46 1.66 4.98-2.32 8.52-12.3 12.68-2.7 1.13-16.25 6.18-20 7.73-7.86 3.24-13.93 6.42-18.87 10.15-13.02 9.84-18.36 11.93-23.71 9.68a24.67 24.67 0 0 1-3.62-1.98l-1.99-1.28a90.4 90.4 0 0 0-2.24-1.4c-3.33-2-2.82-4.28.85-7.34 1.35-1.13 10.66-7.61 13.53-9.91 7.1-5.69 11.91-11.47 14.41-18.34 3.07-8.45 4.89-12.1 6.8-13.39 1.73-1.16 3.36-.53 6.18 1.9.63.56 3.4 3.08 4.11 3.7 1.93 1.7 3.71 3.15 5.67 4.55zm-.6.8c-1.98-1.42-3.79-2.88-5.74-4.6-.73-.64-3.48-3.16-4.1-3.7-2.5-2.16-3.75-2.65-4.97-1.83-1.66 1.11-3.44 4.7-6.42 12.9-2.57 7.07-7.5 12.99-14.72 18.78-2.91 2.33-12.21 8.8-13.52 9.9-3.22 2.68-3.56 4.17-.97 5.72l2.26 1.4 1.99 1.28c1.47.93 2.48 1.5 3.47 1.91 4.9 2.07 9.96.07 22.72-9.56 5.02-3.79 11.15-7 19.1-10.28 3.76-1.55 17.3-6.6 20-7.72 9.5-3.97 13.14-7.2 11.73-11.44-.9-2.71-2.25-3.8-4.3-3.79-1.6 0-2.15.17-7.36 2.05-8.17 2.94-13.34 3.14-19.16-1.01z'/%3E%3C/svg%3E")}.after\:ml-0\.5:after{content:"";margin-left:.125rem}.after\:ml-0:after{content:"";margin-left:0}.after\:text-red-500:after{--tw-text-opacity:1;color:rgba(239,68,68,var(--tw-text-opacity));content:""}.after\:content-\[\'\*\'\]:after{content:"*"}.focus-within\:z-10:focus-within{z-index:10}.focus-within\:text-gray-600:focus-within{--tw-text-opacity:1;color:rgba(75,85,99,var(--tw-text-opacity))}.focus-within\:ring-2:focus-within{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-within\:ring-inset:focus-within{--tw-ring-inset:inset}.focus-within\:ring-indigo-500:focus-within{--tw-ring-opacity:1;--tw-ring-color:rgba(99,102,241,var(--tw-ring-opacity))}.focus-within\:ring-teal-500:focus-within{--tw-ring-opacity:1;--tw-ring-color:rgba(1,157,187,var(--tw-ring-opacity))}.focus-within\:ring-pink-500:focus-within{--tw-ring-opacity:1;--tw-ring-color:rgba(236,72,153,var(--tw-ring-opacity))}.focus-within\:ring-offset-2:focus-within{--tw-ring-offset-width:2px}.focus-within\:ring-offset-gray-100:focus-within{--tw-ring-offset-color:#f3f4f6}.hover\:-translate-y-1:hover{--tw-translate-y:-0.25rem;transform:var(--tw-transform)}.hover\:scale-100:hover{--tw-scale-x:1;--tw-scale-y:1;transform:var(--tw-transform)}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.hover\:border-gray-400:hover{--tw-border-opacity:1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgba(79,70,229,var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgba(55,65,81,var(--tw-bg-opacity))}.hover\:bg-red-500:hover{--tw-bg-opacity:1;background-color:rgba(239,68,68,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgba(5,150,105,var(--tw-bg-opacity))}.hover\:bg-yellow-300:hover{--tw-bg-opacity:1;background-color:rgba(252,211,77,var(--tw-bg-opacity))}.hover\:bg-teal-100:hover,.hover\:bg-teal-700:hover{--tw-bg-opacity:1;background-color:rgba(1,157,187,var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgba(99,102,241,var(--tw-bg-opacity))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.hover\:bg-teal-900:hover{--tw-bg-opacity:1;background-color:rgba(1,157,187,var(--tw-bg-opacity))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgba(156,163,175,var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgba(185,28,28,var(--tw-bg-opacity))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgba(75,85,99,var(--tw-bg-opacity))}.hover\:bg-purple-600:hover{--tw-bg-opacity:1;background-color:rgba(124,58,237,var(--tw-bg-opacity))}.hover\:bg-opacity-50:hover{--tw-bg-opacity:0.5}.hover\:from-indigo-700:hover{--tw-gradient-from:#4338ca;--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to,rgba(67,56,202,0))}.hover\:to-teal-700:hover{--tw-gradient-to:#019dbb}.hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.hover\:text-gray-400:hover{--tw-text-opacity:1;color:rgba(156,163,175,var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgba(17,24,39,var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.hover\:text-indigo-500:hover{--tw-text-opacity:1;color:rgba(99,102,241,var(--tw-text-opacity))}.hover\:text-red-700:hover{--tw-text-opacity:1;color:rgba(185,28,28,var(--tw-text-opacity))}.hover\:text-teal-900:hover{--tw-text-opacity:1;color:rgba(1,157,187,var(--tw-text-opacity))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgba(49,46,129,var(--tw-text-opacity))}.hover\:text-black:hover{--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.hover\:underline:hover{text-decoration:underline}.hover\:drop-shadow-xl:hover{--tw-drop-shadow:drop-shadow(0 20px 13px rgba(0,0,0,0.03)) drop-shadow(0 8px 5px rgba(0,0,0,0.08));filter:var(--tw-filter)}.focus\:z-10:focus{z-index:10}.focus\:border-blue-300:focus{--tw-border-opacity:1;border-color:rgba(147,197,253,var(--tw-border-opacity))}.focus\:border-gray-900:focus{--tw-border-opacity:1;border-color:rgba(17,24,39,var(--tw-border-opacity))}.focus\:border-indigo-300:focus{--tw-border-opacity:1;border-color:rgba(165,180,252,var(--tw-border-opacity))}.focus\:border-red-700:focus{--tw-border-opacity:1;border-color:rgba(185,28,28,var(--tw-border-opacity))}.focus\:border-indigo-700:focus{--tw-border-opacity:1;border-color:rgba(67,56,202,var(--tw-border-opacity))}.focus\:border-gray-300:focus{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.focus\:border-transparent:focus{border-color:transparent}.focus\:border-teal-500:focus{--tw-border-opacity:1;border-color:rgba(1,157,187,var(--tw-border-opacity))}.focus\:border-indigo-500:focus{--tw-border-opacity:1;border-color:rgba(99,102,241,var(--tw-border-opacity))}.focus\:border-white:focus{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.focus\:border-gray-500:focus{--tw-border-opacity:1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.focus\:bg-indigo-600:focus{--tw-bg-opacity:1;background-color:rgba(79,70,229,var(--tw-bg-opacity))}.focus\:bg-red-600:focus{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.focus\:bg-gray-100:focus{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.focus\:bg-indigo-100:focus{--tw-bg-opacity:1;background-color:rgba(224,231,255,var(--tw-bg-opacity))}.focus\:bg-gray-50:focus{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.focus\:text-gray-700:focus{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.focus\:text-indigo-800:focus{--tw-text-opacity:1;color:rgba(55,48,163,var(--tw-text-opacity))}.focus\:text-gray-800:focus{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.focus\:text-gray-500:focus{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.focus\:text-indigo-600:focus{--tw-text-opacity:1;color:rgba(79,70,229,var(--tw-text-opacity))}.focus\:text-indigo-500:focus{--tw-text-opacity:1;color:rgba(99,102,241,var(--tw-text-opacity))}.focus\:placeholder-gray-400:focus::-moz-placeholder{--tw-placeholder-opacity:1;color:rgba(156,163,175,var(--tw-placeholder-opacity))}.focus\:placeholder-gray-400:focus:-ms-input-placeholder{--tw-placeholder-opacity:1;color:rgba(156,163,175,var(--tw-placeholder-opacity))}.focus\:placeholder-gray-400:focus::placeholder{--tw-placeholder-opacity:1;color:rgba(156,163,175,var(--tw-placeholder-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-2:focus,.focus\:ring:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus,.focus\:ring-1:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-inset:focus{--tw-ring-inset:inset}.focus\:ring-gray-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(209,213,219,var(--tw-ring-opacity))}.focus\:ring-indigo-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(199,210,254,var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(254,202,202,var(--tw-ring-opacity))}.focus\:ring-blue-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(191,219,254,var(--tw-ring-opacity))}.focus\:ring-white:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(255,255,255,var(--tw-ring-opacity))}.focus\:ring-teal-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(1,157,187,var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(99,102,241,var(--tw-ring-opacity))}.focus\:ring-sky-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(14,165,233,var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(79,70,229,var(--tw-ring-opacity))}.focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(239,68,68,var(--tw-ring-opacity))}.focus\:ring-pink-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(236,72,153,var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(107,114,128,var(--tw-ring-opacity))}.focus\:ring-green-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgba(16,185,129,var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity:0.5}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-gray-100:focus{--tw-ring-offset-color:#f3f4f6}.focus\:ring-offset-gray-50:focus{--tw-ring-offset-color:#f9fafb}.active\:bg-gray-100:active{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.active\:bg-gray-900:active{--tw-bg-opacity:1;background-color:rgba(17,24,39,var(--tw-bg-opacity))}.active\:bg-red-600:active{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.active\:bg-gray-50:active{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.active\:bg-purple-600:active{--tw-bg-opacity:1;background-color:rgba(124,58,237,var(--tw-bg-opacity))}.active\:text-gray-700:active{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.active\:text-gray-500:active{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.active\:text-gray-800:active{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.disabled\:opacity-25:disabled{opacity:.25}.group:hover .group-hover\:bg-gray-200{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.group:hover .group-hover\:text-gray-500{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}.group:hover .group-hover\:text-gray-400{--tw-text-opacity:1;color:rgba(156,163,175,var(--tw-text-opacity))}.group:hover .group-hover\:text-gray-700{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.group:hover .group-hover\:text-indigo-600{--tw-text-opacity:1;color:rgba(79,70,229,var(--tw-text-opacity))}.group:hover .group-hover\:text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.group:hover .group-hover\:opacity-75{opacity:.75}@media (min-width:640px){.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-1{grid-column:span 1/span 1}.sm\:col-span-9{grid-column:span 9/span 9}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:mx-0{margin-left:0;margin-right:0}.sm\:mx-auto{margin-left:auto;margin-right:auto}.sm\:-my-px{margin-bottom:-1px;margin-top:-1px}.sm\:mx-2{margin-left:.5rem;margin-right:.5rem}.sm\:ml-3{margin-left:.75rem}.sm\:-mr-2{margin-right:-.5rem}.sm\:mt-0{margin-top:0}.sm\:ml-4{margin-left:1rem}.sm\:ml-10{margin-left:2.5rem}.sm\:ml-6{margin-left:1.5rem}.sm\:mt-16{margin-top:4rem}.sm\:-mt-16{margin-top:-4rem}.sm\:mt-2{margin-top:.5rem}.sm\:-ml-10{margin-left:-2.5rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:inline-grid{display:inline-grid}.sm\:hidden{display:none}.sm\:h-10{height:2.5rem}.sm\:h-12{height:3rem}.sm\:h-32{height:8rem}.sm\:w-10{width:2.5rem}.sm\:w-full{width:100%}.sm\:w-auto{width:auto}.sm\:w-12{width:3rem}.sm\:w-32{width:8rem}.sm\:min-w-0{min-width:0}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-xl{max-width:36rem}.sm\:max-w-2xl{max-width:42rem}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-4xl{max-width:56rem}.sm\:max-w-5xl{max-width:64rem}.sm\:max-w-6xl{max-width:72rem}.sm\:max-w-7xl{max-width:80rem}.sm\:max-w-none{max-width:none}.sm\:flex-1{flex:1 1 0%}.sm\:flex-shrink-0{flex-shrink:0}.sm\:translate-y-0{--tw-translate-y:0px;transform:var(--tw-transform)}.sm\:translate-x-2{--tw-translate-x:0.5rem;transform:var(--tw-transform)}.sm\:translate-x-0{--tw-translate-x:0px;transform:var(--tw-transform)}.sm\:scale-95{--tw-scale-x:.95;--tw-scale-y:.95;transform:var(--tw-transform)}.sm\:scale-100{--tw-scale-x:1;--tw-scale-y:1;transform:var(--tw-transform)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:flex-wrap{flex-wrap:wrap}.sm\:items-start{align-items:flex-start}.sm\:items-end{align-items:flex-end}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-end{justify-content:flex-end}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-8{gap:2rem}.sm\:gap-5{gap:1.25rem}.sm\:gap-4{gap:1rem}.sm\:gap-px{gap:1px}.sm\:gap-6{gap:1.5rem}.sm\:gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}.sm\:space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.25rem*var(--tw-space-x-reverse))}.sm\:space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.sm\:divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.sm\:divide-y-0>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(0px*var(--tw-divide-y-reverse));border-top-width:calc(0px*(1 - var(--tw-divide-y-reverse)))}.sm\:divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(229,231,235,var(--tw-divide-opacity))}.sm\:rounded-lg{border-radius:.5rem}.sm\:rounded-md{border-radius:.375rem}.sm\:rounded-2xl{border-radius:1rem}.sm\:rounded-tl-md{border-top-left-radius:.375rem}.sm\:rounded-tr-md{border-top-right-radius:.375rem}.sm\:rounded-bl-md{border-bottom-left-radius:.375rem}.sm\:rounded-br-md{border-bottom-right-radius:.375rem}.sm\:p-6{padding:1.5rem}.sm\:p-8{padding:2rem}.sm\:p-0{padding:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-20{padding-left:5rem;padding-right:5rem}.sm\:py-12{padding-bottom:3rem;padding-top:3rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:py-24{padding-bottom:6rem;padding-top:6rem}.sm\:py-5{padding-bottom:1.25rem;padding-top:1.25rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-14{padding-left:3.5rem;padding-right:3.5rem}.sm\:pt-0{padding-top:0}.sm\:pb-4{padding-bottom:1rem}.sm\:pt-1{padding-top:.25rem}.sm\:pr-2{padding-right:.5rem}.sm\:pl-6{padding-left:1.5rem}.sm\:pr-6{padding-right:1.5rem}.sm\:pt-20{padding-top:5rem}.sm\:pb-24{padding-bottom:6rem}.sm\:pt-24{padding-top:6rem}.sm\:pb-64{padding-bottom:16rem}.sm\:pb-1{padding-bottom:.25rem}.sm\:pr-4{padding-right:1rem}.sm\:text-left{text-align:left}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-5xl{font-size:3rem;line-height:1}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:duration-700{transition-duration:.7s}}@media (min-width:768px){.md\:fixed{position:fixed}.md\:inset-y-0{bottom:0;top:0}.md\:order-2{order:2}.md\:order-1{order:1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-start-2{grid-column-start:2}.md\:col-start-4{grid-column-start:4}.md\:mt-0{margin-top:0}.md\:-ml-20{margin-left:-5rem}.md\:ml-0{margin-left:0}.md\:ml-6{margin-left:1.5rem}.md\:-mr-16{margin-right:-4rem}.md\:-ml-16{margin-left:-4rem}.md\:ml-2{margin-left:.5rem}.md\:block{display:block}.md\:inline{display:inline}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:h-screen{height:100vh}.md\:w-64{width:16rem}.md\:w-56{width:14rem}.md\:flex-1{flex:1 1 0%}.md\:flex-shrink-0{flex-shrink:0}.md\:flex-grow{flex-grow:1}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-col{flex-direction:column}.md\:items-center{align-items:center}.md\:justify-start{justify-content:flex-start}.md\:justify-center{justify-content:center}.md\:justify-between{justify-content:space-between}.md\:gap-6{gap:1.5rem}.md\:gap-8{gap:2rem}.md\:gap-4{gap:1rem}.md\:space-x-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2.5rem*var(--tw-space-x-reverse))}.md\:overflow-hidden{overflow:hidden}.md\:overflow-y-auto{overflow-y:auto}.md\:rounded-lg{border-radius:.5rem}.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:p-20{padding:5rem}.md\:p-12{padding:3rem}.md\:py-0{padding-bottom:0;padding-top:0}.md\:px-12{padding-left:3rem;padding-right:3rem}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:px-0{padding-left:0;padding-right:0}.md\:pl-20{padding-left:5rem}.md\:pl-64{padding-left:16rem}}@media (min-width:1024px){.lg\:absolute{position:absolute}.lg\:relative{position:relative}.lg\:left-1\/2{left:50%}.lg\:left-0{left:0}.lg\:right-0{right:0}.lg\:bottom-0{bottom:0}.lg\:top-auto{top:auto}.lg\:order-first{order:-9999}.lg\:order-last{order:9999}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-start-2{grid-column-start:2}.lg\:col-start-1{grid-column-start:1}.lg\:m-0{margin:0}.lg\:mx-auto{margin-left:auto;margin-right:auto}.lg\:mx-0{margin-right:0}.lg\:ml-0,.lg\:mx-0{margin-left:0}.lg\:mt-0{margin-top:0}.lg\:mt-16{margin-top:4rem}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:h-full{height:100%}.lg\:h-1{height:.25rem}.lg\:h-48{height:12rem}.lg\:w-0{width:0}.lg\:w-auto{width:auto}.lg\:w-full{width:100%}.lg\:max-w-7xl{max-width:80rem}.lg\:max-w-2xl{max-width:42rem}.lg\:max-w-none{max-width:none}.lg\:flex-1{flex:1 1 0%}.lg\:flex-shrink-0{flex-shrink:0}.lg\:-translate-x-1\/2{--tw-translate-x:-50%;transform:var(--tw-transform)}.lg\:grid-flow-col-dense{grid-auto-flow:column dense}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:items-center{align-items:center}.lg\:justify-between{justify-content:space-between}.lg\:gap-24{gap:6rem}.lg\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.lg\:gap-y-16{row-gap:4rem}.lg\:rounded-none{border-radius:0}.lg\:border-0{border-width:0}.lg\:border-b{border-bottom-width:1px}.lg\:border-l{border-left-width:1px}.lg\:border-r{border-right-width:1px}.lg\:border-gray-200{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-12{padding-bottom:3rem;padding-top:3rem}.lg\:py-16{padding-bottom:4rem;padding-top:4rem}.lg\:px-0{padding-left:0;padding-right:0}.lg\:py-32{padding-bottom:8rem;padding-top:8rem}.lg\:pt-24{padding-top:6rem}.lg\:pl-9{padding-left:2.25rem}.lg\:pl-8{padding-left:2rem}.lg\:pb-8{padding-bottom:2rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}}@media (min-width:1280px){.xl\:absolute{position:absolute}.xl\:relative{position:relative}.xl\:inset-0{left:0;right:0}.xl\:inset-0,.xl\:inset-y-0{bottom:0;top:0}.xl\:top-0{top:0}.xl\:left-0{left:0}.xl\:order-last{order:9999}.xl\:col-span-2{grid-column:span 2/span 2}.xl\:col-start-2{grid-column-start:2}.xl\:col-start-1{grid-column-start:1}.xl\:mt-0{margin-top:0}.xl\:mt-2{margin-top:.5rem}.xl\:grid{display:grid}.xl\:h-full{height:100%}.xl\:w-32{width:8rem}.xl\:grid-flow-col-dense{grid-auto-flow:column dense}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:gap-8{gap:2rem}.xl\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.xl\:overflow-y-auto{overflow-y:auto}.xl\:bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.xl\:px-0{padding-left:0;padding-right:0}.xl\:pb-24{padding-bottom:6rem}}@media (min-width:1536px){.\32xl\:mt-5{margin-top:1.25rem}.\32xl\:block{display:block}.\32xl\:hidden{display:none}} diff --git a/public/img/FSU-Jena-logo.jpg b/public/img/FSU-Jena-logo.jpg new file mode 100644 index 000000000..7f825a576 Binary files /dev/null and b/public/img/FSU-Jena-logo.jpg differ diff --git a/public/img/UniversiteDeGeneve.png b/public/img/UniversiteDeGeneve.png new file mode 100644 index 000000000..0d428d80f Binary files /dev/null and b/public/img/UniversiteDeGeneve.png differ diff --git a/public/img/UniversiteParisSaclay.png b/public/img/UniversiteParisSaclay.png new file mode 100644 index 000000000..423854abe Binary files /dev/null and b/public/img/UniversiteParisSaclay.png differ diff --git a/public/img/eln/nobs.png b/public/img/eln/nobs.png new file mode 100644 index 000000000..f8ffa0f82 Binary files /dev/null and b/public/img/eln/nobs.png differ diff --git a/public/img/imbe-logo.png b/public/img/imbe-logo.png new file mode 100644 index 000000000..baff06f93 Binary files /dev/null and b/public/img/imbe-logo.png differ diff --git a/public/img/instrument-format.png b/public/img/instrument-format.png new file mode 100644 index 000000000..fa03a1564 Binary files /dev/null and b/public/img/instrument-format.png differ diff --git a/public/img/metadata-format.png b/public/img/metadata-format.png new file mode 100644 index 000000000..014c90c4e Binary files /dev/null and b/public/img/metadata-format.png differ diff --git a/public/img/molecule-formats.png b/public/img/molecule-formats.png new file mode 100644 index 000000000..ad00d5477 Binary files /dev/null and b/public/img/molecule-formats.png differ diff --git a/public/img/nmrxiv-logo.png b/public/img/nfdi4chem-logo.png similarity index 100% rename from public/img/nmrxiv-logo.png rename to public/img/nfdi4chem-logo.png diff --git a/public/img/old-logo.svg b/public/img/old-logo.svg deleted file mode 100644 index 8350812bf..000000000 --- a/public/img/old-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/public/img/ph.jpg b/public/img/ph.jpg deleted file mode 100644 index a987c2619..000000000 Binary files a/public/img/ph.jpg and /dev/null differ diff --git a/public/img/spectra-format.png b/public/img/spectra-format.png new file mode 100644 index 000000000..85a7f6cf6 Binary files /dev/null and b/public/img/spectra-format.png differ diff --git a/public/img/spectra/1.png b/public/img/spectra/1.png new file mode 100644 index 000000000..222de0883 Binary files /dev/null and b/public/img/spectra/1.png differ diff --git a/public/img/spectra/2.png b/public/img/spectra/2.png new file mode 100644 index 000000000..a9d452f08 Binary files /dev/null and b/public/img/spectra/2.png differ diff --git a/public/img/spectra/3.png b/public/img/spectra/3.png new file mode 100644 index 000000000..2bab4eb8c Binary files /dev/null and b/public/img/spectra/3.png differ diff --git a/public/img/spectra/4.png b/public/img/spectra/4.png new file mode 100644 index 000000000..188a43a59 Binary files /dev/null and b/public/img/spectra/4.png differ diff --git a/public/js/app.js b/public/js/app.js index 27433a8b3..227dc30f6 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -38472,14 +38472,14 @@ var footerNavigation = { href: "https://docs.nmrxiv.org" }, { name: "Guides", - href: "https://docs.nmrxiv.org/docs/submission-guides/overview" + href: "https://docs.nmrxiv.org/submission-guides/overview" }, { name: "API Status", - href: "https://docs.nmrxiv.org/docs/developer-guides/API" + href: "https://docs.nmrxiv.org/developer-guides/API" }], About: [{ name: "Adivsory Board", - href: "https://docs.nmrxiv.org/docs/contributing/contributors-and-steering-committee" + href: "https://docs.nmrxiv.org/contributing/contributors-and-steering-committee" } // { name: "Metrics", href: "#" }, // { name: "Blog", href: "#" }, // { name: "Press", href: "#" }, diff --git a/resources/css/app.css b/resources/css/app.css index 02cea7ca0..d7f62d5cc 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -5,21 +5,31 @@ .tooltip { position: relative; - display: inline-block; + display: inline-flex; + align-items: center; + justify-content: center; font-size: 0.8em; } .tooltip .tooltiptext { visibility: hidden; + opacity: 0; position: absolute; - z-index: 100; - bottom: 100%; + z-index: 9999; + top: calc(100% + 0.25rem); + bottom: auto; left: 50%; - margin-left: -60px; + transform: translateX(-50%); + margin-left: 0; + transition: opacity 0.12s ease-out, visibility 0.12s ease-out; + pointer-events: none; } -.tooltip:hover .tooltiptext { +.tooltip:hover .tooltiptext, +.tooltip:focus-within .tooltiptext { visibility: visible; + opacity: 1; + pointer-events: auto; } @layer utilities { @@ -32,6 +42,11 @@ -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } + + /* Sortable / vuedraggable: chosenClass must be a single DOM token (no spaces). */ + .publish-author-draggable-chosen { + @apply ring-2 ring-primary-200; + } } .tooltip .tooltiptextbottom { @@ -49,13 +64,15 @@ } .tooltip .tooltiptextright { - display: flex; + display: inline-flex; visibility: hidden; position: absolute; z-index: 1; left: 100%; top: 0%; margin: auto; + white-space: nowrap; + width: max-content; } .tooltip:hover .tooltiptextright { @@ -105,10 +122,9 @@ } .vue-tags-input.ti-focus .ti-input { - border-color: rgba(209, 213, 219, var(--tw-border-opacity)); + border-color: #0088a0; border-width: 1px; - --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + box-shadow: 0 0 0 1px rgb(0 136 160 / 0.2), 0 1px 2px 0 rgba(0, 0, 0, 0.05); } .ti-tag[data-v-2fbda277] { @@ -116,6 +132,21 @@ font-size: 1em !important; } +/* @sipec/vue3-tags-input — library default $primary is #5C6BC0 (indigo); align chips & autocomplete with brand */ +.vue-tags-input .ti-tag:not(.ti-invalid):not(.ti-deletion-mark) { + background-color: #0088a0; + color: #fff; +} + +.vue-tags-input .ti-autocomplete .ti-selected-item { + background-color: #0088a0; + color: #fff; +} + +.vue-tags-input .ti-autocomplete .ti-item > div:hover { + background-color: rgb(0 136 160 / 0.1); +} + .ti-new-tag-input-wrapper input:focus { outline: none !important; outline: 0px solid transparent; @@ -124,6 +155,14 @@ border-color: transparent; } +/* @vuepic/vue-datepicker — align light theme with nmrXiv brand (overrides default Material blue) */ +.dp__theme_light { + --dp-primary-color: #0088a0; + --dp-primary-disabled-color: #019dbb; + --dp-primary-text-color: #ffffff; + --dp-highlight-color: rgb(0 136 160 / 12%); +} + .index_beams { background-color: #ffffff; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600' viewBox='0 0 600 600'%3E%3Cpath fill='%23009ebb' fill-opacity='0.08' d='M600 325.1v-1.17c-6.5 3.83-13.06 7.64-14.68 8.64-10.6 6.56-18.57 12.56-24.68 19.09-5.58 5.95-12.44 10.06-22.42 14.15-1.45.6-2.96 1.2-4.83 1.9l-4.75 1.82c-9.78 3.75-14.8 6.27-18.98 10.1-4.23 3.88-9.65 6.6-16.77 8.84-1.95.6-3.99 1.17-6.47 1.8l-6.14 1.53c-5.29 1.35-8.3 2.37-10.54 3.78-3.08 1.92-6.63 3.26-12.74 5.03a384.1 384.1 0 0 1-4.82 1.36c-2.04.58-3.6 1.04-5.17 1.52a110.03 110.03 0 0 0-11.2 4.05c-2.7 1.15-5.5 3.93-8.78 8.4a157.68 157.68 0 0 0-6.15 9.2c-5.75 9.07-7.58 11.74-10.24 14.51a50.97 50.97 0 0 1-4.6 4.22c-2.33 1.9-10.39 7.54-11.81 8.74a14.68 14.68 0 0 0-3.67 4.15c-1.24 2.3-1.9 4.57-2.78 8.87-2.17 10.61-3.52 14.81-8.2 22.1-4.07 6.33-6.8 9.88-9.83 12.99-.47.48-.95.96-1.5 1.48l-3.75 3.56c-1.67 1.6-3.18 3.12-4.86 4.9a42.44 42.44 0 0 0-9.89 16.94c-2.5 8.13-2.72 15.47-1.76 27.22.47 5.82.51 6.36.51 8.18 0 10.51.12 17.53.63 25.78.24 4.05.56 7.8.97 11.22h.9c-1.13-9.58-1.5-21.83-1.5-37 0-1.86-.04-2.4-.52-8.26-.94-11.63-.72-18.87 1.73-26.85a41.44 41.44 0 0 1 9.65-16.55c1.67-1.76 3.18-3.27 4.83-4.85.63-.6 3.13-2.96 3.75-3.57a71.6 71.6 0 0 0 1.52-1.5c3.09-3.16 5.86-6.76 9.96-13.15 4.77-7.42 6.15-11.71 8.34-22.44.86-4.21 1.5-6.4 2.68-8.6.68-1.25 1.79-2.48 3.43-3.86 1.38-1.15 9.43-6.8 11.8-8.72 1.71-1.4 3.26-2.81 4.7-4.3 2.72-2.85 4.56-5.54 10.36-14.67a156.9 156.9 0 0 1 6.1-9.15c3.2-4.33 5.9-7.01 8.37-8.07 3.5-1.5 7.06-2.77 11.1-4.02a233.84 233.84 0 0 1 7.6-2.2l2.38-.67c6.19-1.79 9.81-3.16 12.98-5.15 2.14-1.33 5.08-2.33 10.27-3.65l6.14-1.53c2.5-.63 4.55-1.2 6.52-1.82 7.24-2.27 12.79-5.06 17.15-9.05 4.05-3.72 9-6.2 18.66-9.9l4.75-1.82c1.87-.72 3.39-1.31 4.85-1.91 10.1-4.15 17.07-8.32 22.76-14.4 6.05-6.45 13.95-12.4 24.49-18.92 1.56-.96 7.82-4.6 14.15-8.33v-64.58c-4 8.15-8.52 14.85-12.7 17.9-2.51 1.82-5.38 4.02-9.04 6.92a1063.87 1063.87 0 0 0-6.23 4.98l-1.27 1.02a2309.25 2309.25 0 0 1-4.87 3.9c-7.55 6-12.9 10.05-17.61 13.19-3.1 2.06-3.86 2.78-8.06 7.13-5.84 6.07-11.72 8.62-29.15 10.95-11.3 1.5-20.04 4.91-30.75 11.07-1.65.94-7.27 4.27-6.97 4.1-2.7 1.58-4.69 2.69-6.64 3.66-5.63 2.8-10.47 4.17-15.71 4.17-17.13 0-41.44 11.51-51.63 22.83-12.05 13.4-31.42 27.7-45.25 31.16-7.4 1.85-11.85 7.05-14.04 14.69-1.26 4.4-1.58 8.28-1.58 13.82 0 .82.01.98.24 3.63.45 5.18.35 8.72-.77 13.26-1.53 6.2-4.89 12.6-10.59 19.43-13.87 16.65-22.88 46.58-22.88 71.68 0 2.39.02 4.26.06 8.75.12 10.8.1 15.8-.22 21.95-.56 11.18-2.09 20.73-5 29.3h-1.05c2.94-8.56 4.49-18.12 5.05-29.35.31-6.13.34-11.1.22-21.9-.04-4.48-.06-6.36-.06-8.75 0-25.32 9.07-55.47 23.12-72.32 5.6-6.72 8.88-12.99 10.38-19.03 1.09-4.4 1.18-7.85.74-12.93-.23-2.7-.24-2.86-.24-3.72 0-5.62.32-9.57 1.62-14.1 2.28-7.95 6.97-13.44 14.76-15.39 13.6-3.4 32.82-17.59 44.75-30.84C409 360.14 433.58 348.5 451 348.5c5.07 0 9.77-1.33 15.26-4.07 1.93-.96 3.9-2.05 6.58-3.62-.3.18 5.33-3.16 6.98-4.11 10.82-6.21 19.66-9.67 31.11-11.2 17.23-2.3 22.9-4.75 28.57-10.64 4.25-4.41 5.04-5.16 8.22-7.28 4.68-3.11 10.01-7.14 17.55-13.14a1113.33 1113.33 0 0 0 4.86-3.89l1.28-1.02a4668.54 4668.54 0 0 1 6.23-4.98c3.67-2.9 6.55-5.12 9.07-6.95 4.37-3.19 9.16-10.56 13.29-19.4v66.9zm0-116.23c-.62.01-1.27.06-1.95.13-6.13.63-13.83 3.45-21.83 7.45-3.64 1.82-8.46 2.67-14.17 2.71-4.7.04-9.72-.47-14.73-1.33-1.7-.3-3.26-.61-4.67-.93a31.55 31.55 0 0 0-3.55-.57 273.4 273.4 0 0 0-16.66-.88c-10.42-.16-17.2.74-17.97 2.73-.38.97.6 2.55 3.03 4.87 1.01.97 2.22 2.03 4.04 3.55a1746.07 1746.07 0 0 0 4.79 4.02c1.39 1.2 3.1 1.92 5.5 2.5.7.16.86.2 2.64.54 3.53.7 5.03 1.25 6.15 2.63 1.41 1.76 1.4 4.54-.15 8.88-2.44 6.83-5.72 10.05-10.19 10.33-3.63.23-7.6-1.29-14.52-5.06-4.53-2.47-6.82-7.3-8.32-15.26-.17-.87-.32-1.78-.5-2.86l-.43-2.76c-1.05-6.58-1.9-9.2-3.73-10.11-.81-.4-1.59-.74-2.36-1-2.27-.77-4.6-1.02-8.1-.92-2.29.07-14.7 1-13.77.93-20.55 1.37-28.8 5.05-37.09 14.99a133.07 133.07 0 0 0-4.25 5.44l-2.3 3.09-2.51 3.32c-4.1 5.36-7.06 8.48-10.39 11.12-.65.52-1.33 1.04-2.13 1.62l-4.11 2.94a106.8 106.8 0 0 0-5.16 3.99c-4.55 3.74-9.74 8.6-16.25 15.38-8.25 8.58-11.78 13.54-11.7 15.95.07 1.65 1.64 2.11 6.79 2.38 1.61.09 2.15.12 2.98.2 2.95.24 5.09.73 6.81 1.68 7.48 4.15 11.63 7.26 13.95 11.58 3.3 6.15.8 12.88-8.89 20.26-8.28 6.3-11.1 10.37-11.31 14.96-.06 1.17 0 1.93.26 4.43.69 6.47.25 10.65-2.8 17.42a44.23 44.23 0 0 1-4.16 7.53c-2.82 3.97-5.47 5.74-10.6 7.69-.43.16-3.34 1.23-4.27 1.59-1.8.68-3.38 1.36-5.01 2.14-4.18 2-8.4 4.6-13.1 8.24-8.44 6.51-13.23 14.56-15.98 25.06-1.1 4.2-1.55 6.81-2.8 15.21-1.26 8.6-2.17 12.64-4.08 16.55-2.1 4.28-11.93 26.59-12.97 28.88a382.7 382.7 0 0 1-6.37 13.41c-4.07 8.11-7.61 14.07-10.73 17.81-5.38 6.46-8.98 14.37-13.77 28.42a810.14 810.14 0 0 0-1.89 5.6c-1.8 5.35-2.96 8.6-4.26 11.85-6.13 15.32-25.43 26.31-46.46 26.31-11.2 0-20.58-2.74-31.02-8.55-5.6-3.13-4.55-2.42-22.26-14.54-14.33-9.8-17.7-10.73-20.47-6.9-.37.5-1.81 2.74-1.83 2.77a52.24 52.24 0 0 1-4.94 5.9c-.73.79-5.52 5.87-6.97 7.45-2.38 2.6-4.3 4.81-5.98 6.93a45.6 45.6 0 0 0-5.08 7.66c-1.29 2.57-1.9 5.25-2.66 10.6a997.6 997.6 0 0 1-.46 3.18h-1l.47-3.32c.77-5.45 1.4-8.2 2.75-10.9a46.54 46.54 0 0 1 5.2-7.84c1.7-2.14 3.63-4.38 6.03-6.98 1.45-1.59 6.24-6.68 6.96-7.46a51.58 51.58 0 0 0 4.84-5.78s1.47-2.26 1.86-2.8c3.25-4.5 7.08-3.44 21.84 6.67 17.67 12.08 16.62 11.38 22.19 14.48 10.3 5.73 19.5 8.43 30.53 8.43 20.65 0 39.57-10.77 45.54-25.69a219.7 219.7 0 0 0 4.24-11.8 6752.32 6752.32 0 0 0 1.88-5.6c4.83-14.16 8.47-22.14 13.96-28.73 3.05-3.66 6.56-9.57 10.6-17.61 1.97-3.93 4.04-8.31 6.35-13.38 1.03-2.28 10.88-24.61 12.98-28.91 1.85-3.79 2.75-7.76 4-16.25 1.24-8.44 1.7-11.07 2.81-15.32 2.8-10.7 7.71-18.94 16.33-25.6a73.18 73.18 0 0 1 13.29-8.35c1.66-.8 3.27-1.48 5.08-2.18.94-.36 3.86-1.43 4.28-1.59 4.95-1.88 7.44-3.55 10.14-7.33 1.35-1.9 2.68-4.3 4.06-7.37 2.97-6.58 3.39-10.59 2.72-16.9a27.13 27.13 0 0 1-.27-4.58c.22-4.94 3.21-9.24 11.7-15.7 9.33-7.11 11.66-13.34 8.62-19-2.2-4.09-6.25-7.12-13.55-11.17-1.57-.88-3.6-1.33-6.42-1.57-.8-.07-1.34-.1-2.95-.19-5.77-.3-7.63-.85-7.72-3.34-.1-2.81 3.5-7.87 11.97-16.69 6.53-6.8 11.75-11.69 16.33-15.45 1.79-1.47 3.42-2.72 5.2-4.03l4.12-2.94c.79-.58 1.46-1.08 2.1-1.59 3.26-2.6 6.16-5.65 10.21-10.94a383.2 383.2 0 0 0 2.5-3.32l2.31-3.09c1.8-2.39 3.04-4 4.29-5.48 8.47-10.17 16.98-13.96 37.27-15.3-.44.02 12-.9 14.32-.98 3.62-.1 6.05.16 8.46.98.8.27 1.62.62 2.47 1.04 2.27 1.14 3.17 3.87 4.27 10.85l.44 2.76c.17 1.07.33 1.97.5 2.83 1.44 7.69 3.62 12.29 7.8 14.57 6.76 3.68 10.6 5.15 13.99 4.94 4-.25 6.99-3.17 9.3-9.67 1.45-4.04 1.46-6.49.32-7.92-.9-1.12-2.28-1.62-5.57-2.27a55.8 55.8 0 0 1-2.67-.55c-2.54-.6-4.39-1.4-5.93-2.71a252.63 252.63 0 0 0-4.78-4.01 84.35 84.35 0 0 1-4.08-3.6c-2.73-2.6-3.86-4.43-3.28-5.95 1.02-2.64 7.82-3.54 18.93-3.37a230.56 230.56 0 0 1 16.73.88c2.76.39 3.2.49 3.68.6 1.4.3 2.95.62 4.62.91a82.9 82.9 0 0 0 14.56 1.32c5.56-.04 10.24-.86 13.73-2.6 8.1-4.05 15.89-6.9 22.17-7.56.7-.07 1.4-.11 2.05-.13v1zm0-100.94v1.5c-8.62 16.05-17.27 29.55-23.65 35.92-3.19 3.2-7.62 4.9-13.54 5.56-4.45.48-8.28.4-19.18-.2-9.91-.55-15.32-.44-20.52.78a84.05 84.05 0 0 1-15 2.11l-2.25.14c-12.49.75-19.37 1.78-32.72 5.74-4.5 1.33-9.27 2.49-14.3 3.48a246.27 246.27 0 0 1-32.6 3.97c-7.56.45-13.21.57-20.24.57-5.4 0-11.9 1.61-18 5.18-8.3 4.87-15.06 12.87-19.53 24.5a68.57 68.57 0 0 1-4.56 9.8c-3.6 6.2-6.92 8.99-13.38 12.18l-4.03 1.96a64.48 64.48 0 0 0-15.16 10.25c-8.2 7.33-13.72 16.63-22.54 35.6l-2.08 4.49c-7.3 15.7-11.5 23.3-17.35 29.87-7.7 8.66-20.25 14.42-40.31 20.08-4.37 1.23-19.04 5.08-19.24 5.13-6.92 1.87-11.68 3.34-15.63 4.92-10.55 4.22-18.71 10.52-36.38 26.52l-1.7 1.54c-8.58 7.76-13.41 11.9-18.81 15.88-3.95 2.9-8 5.67-12.97 8.91-2.06 1.34-10.3 6.6-12.33 7.94-11.52 7.5-18.53 13.04-24.62 20.08a62.01 62.01 0 0 0-6.44 8.85c-4.13 6.91-6.27 13.15-9.2 25.11l-1.54 6.26c-.6 2.45-1.15 4.54-1.72 6.58-2.97 10.7-6.9 17.36-14.78 26.91L69.6 491a148.51 148.51 0 0 0-4.19 5.3 23.9 23.9 0 0 0-3.44 6.28c-1.16 3.23-1.52 5.9-1.87 11.94-.58 10.05-1.42 15.04-4.63 22.67-1.57 3.72-5.66 14.02-6.41 15.8a73.46 73.46 0 0 1-3.57 7.4c-2.88 5.14-6.71 10.12-13.12 16.95-5.96 6.36-8.87 10.9-10.61 16a56.88 56.88 0 0 0-1.38 4.82l-.46 1.84h-1.03l.52-2.08c.52-2.09.92-3.49 1.4-4.9 1.8-5.25 4.78-9.9 10.84-16.36 6.35-6.78 10.13-11.7 12.97-16.77a72.5 72.5 0 0 0 3.52-7.29c.75-1.76 4.84-12.06 6.4-15.8 3.17-7.5 3.99-12.4 4.56-22.33.35-6.14.72-8.88 1.93-12.23a24.9 24.9 0 0 1 3.58-6.54c1.27-1.7 2.6-3.37 4.22-5.34l4.11-4.95c7.8-9.46 11.66-16 14.59-26.54.56-2.04 1.1-4.12 1.71-6.56l1.53-6.26c2.96-12.04 5.13-18.36 9.32-25.39 1.84-3.08 4-6.05 6.54-8.99 6.17-7.12 13.24-12.7 24.83-20.26 2.05-1.33 10.28-6.6 12.33-7.94 4.96-3.22 9-5.98 12.92-8.87 5.37-3.95 10.19-8.08 18.74-15.82l1.7-1.54c17.76-16.09 25.98-22.43 36.67-26.7 4-1.6 8.8-3.09 15.75-4.96.21-.06 14.87-3.9 19.22-5.13 19.9-5.61 32.32-11.31 39.85-19.78 5.76-6.48 9.93-14.02 17.18-29.64l2.09-4.5c8.87-19.07 14.44-28.46 22.77-35.9a65.48 65.48 0 0 1 15.38-10.4l4.04-1.97c6.3-3.1 9.47-5.77 12.96-11.77a67.6 67.6 0 0 0 4.48-9.67c4.56-11.84 11.47-20.02 19.97-25 6.25-3.66 12.93-5.32 18.5-5.32 7.01 0 12.65-.12 20.17-.57a245.3 245.3 0 0 0 32.47-3.96c5-.98 9.75-2.13 14.22-3.45 13.43-3.98 20.38-5.02 32.94-5.78l2.24-.14c5.76-.37 9.8-.9 14.85-2.09 5.31-1.25 10.79-1.35 22.6-.7 9.04.5 12.84.58 17.21.1 5.71-.62 9.94-2.26 12.95-5.26 6.44-6.45 15.3-20.37 24.35-36.72zm0 450.21c-1.28-4.6-2.2-10.55-3.33-20.25l-.24-2.04-.23-2.03c-1.82-15.7-3.07-21.98-5.55-24.47-2.46-2.46-3.04-5.03-2.52-8.64.1-.6.18-1.1.39-2.15.69-3.54.77-5.04.08-6.84-.91-2.38-3.31-4.41-7.79-6.26-5.08-2.09-6.52-4.84-4.89-8.44.66-1.45 1.79-3.02 3.52-5.01 1.04-1.2 5.48-5.96 5.08-5.53 6.15-6.7 8.98-11.34 8.98-16.48a15.2 15.2 0 0 1 6.5-12.89v1.26a14.17 14.17 0 0 0-5.5 11.63c0 5.47-2.93 10.29-9.24 17.16.38-.42-4.04 4.33-5.07 5.5-1.67 1.93-2.75 3.43-3.36 4.77-1.37 3.04-.23 5.22 4.36 7.1 4.71 1.95 7.32 4.16 8.34 6.83.78 2.04.7 3.67-.03 7.4-.2 1.03-.3 1.51-.38 2.09-.48 3.33.03 5.59 2.23 7.8 2.74 2.74 3.98 8.96 5.84 25.06l.24 2.03.23 2.04c.82 7.01 1.53 12.06 2.34 16.03v4.33zm0-62.16c-1.4-3.13-4.43-9.9-4.95-11.17-1.02-2.53-1.25-3.8-.91-5.18.2-.84 2.05-4.68 2.32-5.33a70.79 70.79 0 0 0 3.54-11.2v3.99a62.82 62.82 0 0 1-2.62 7.6c-.31.75-2.09 4.46-2.27 5.18-.28 1.12-.08 2.22.87 4.57.41 1.02 2.5 5.7 4.02 9.09v2.45zm0-85.09c-1.65 1.66-3.66 2.9-6.4 4.13-.25.1-13.97 5.47-20.4 8.43-9.35 4.32-16.7 5.9-23.03 5.25-5.08-.53-9.02-2.25-14.77-5.92l-3.2-2.07a77.4 77.4 0 0 0-5.44-3.27c-4.05-2.18-3.25-5.8 1.47-10.47 3.71-3.68 9.6-7.93 18.73-13.8l4.46-2.82c17.95-11.33 18.22-11.5 22.27-14.74 11.25-9 19.69-14.02 26.31-15.1v1.02c-6.37 1.1-14.62 6-25.69 14.86-4.1 3.28-4.34 3.44-22.36 14.8a652.4 652.4 0 0 0-4.45 2.83c-9.07 5.83-14.92 10.05-18.57 13.66-4.31 4.28-4.95 7.13-1.7 8.88 1.7.91 3.29 1.88 5.5 3.3l3.2 2.08c5.64 3.59 9.45 5.25 14.34 5.76 6.13.64 13.32-.9 22.52-5.15 6.46-2.98 20.18-8.35 20.4-8.44 3.04-1.37 5.1-2.71 6.81-4.69v1.47zm0-41.37v1c-6.56.26-12.11 3.13-19.71 9.08l-4.63 3.68a51.87 51.87 0 0 1-4.4 3.14c-.82.52-5.51 3.33-6.22 3.76-3.31 2-6.15 3.8-8.87 5.6a112.61 112.61 0 0 0-8.16 5.92c-4.61 3.72-7.4 6.9-7.97 9.35-.63 2.67 1.48 4.53 7.05 5.46 10.7 1.78 20.92-.05 30.45-4.65a61.96 61.96 0 0 0 17.1-12.2 41.8 41.8 0 0 0 5.36-7.42v1.92a38.94 38.94 0 0 1-4.64 6.19 62.95 62.95 0 0 1-17.39 12.41c-9.7 4.68-20.13 6.55-31.05 4.73-6.06-1-8.65-3.29-7.85-6.67.64-2.74 3.53-6.05 8.31-9.9 2.35-1.9 5.1-3.88 8.24-5.97 2.73-1.82 5.58-3.61 8.9-5.62.72-.44 5.4-3.24 6.22-3.75 1.26-.8 2.6-1.76 4.3-3.09.8-.62 3.9-3.1 4.63-3.67 7.77-6.1 13.49-9.04 20.33-9.3zm0-154.6v1c-1.75-.24-4.3.23-7.82 1.55-10.01 3.75-13.8 5.07-19.15 6.76-1.78.56-2.63.83-3.87 1.24-1.48.5-3.16.76-6.74 1.16a1550.34 1550.34 0 0 0-2.64.3c-7.8.94-11.28 2.47-11.28 6.07 0 4.45 2.89 13.18 7.96 25.81a57.34 57.34 0 0 1 2.33 7.6 258.32 258.32 0 0 1 .84 3.46c1.86 7.62 3.17 10.71 5.56 11.67 2.21.88 4.7.6 7.47-.72 3.48-1.69 7.22-4.94 11.2-9.47 1.52-1.7 2.97-3.49 4.59-5.57l3.16-4.1c2.59-3.23 6.07-12.21 8.39-20.23v3.45c-2.29 7.2-5.27 14.5-7.61 17.41-.44.55-2.67 3.46-3.15 4.09-1.63 2.1-3.1 3.9-4.62 5.62-4.08 4.61-7.9 7.94-11.53 9.7-2.99 1.44-5.77 1.75-8.28.74-2.84-1.13-4.2-4.34-6.15-12.35a2097.48 2097.48 0 0 1-.84-3.46c-.8-3.2-1.47-5.45-2.28-7.46-5.14-12.8-8.04-21.55-8.04-26.19 0-4.37 3.84-6.06 12.16-7.07a160.9 160.9 0 0 1 2.65-.3c3.5-.39 5.15-.64 6.53-1.1 1.26-.42 2.1-.7 3.88-1.26 5.34-1.68 9.11-3 19.1-6.74 3.53-1.32 6.22-1.84 8.18-1.61zM0 292c10.13-11.31 18.13-23.2 23.07-35.39 3.3-8.14 6.09-16.12 10.81-30.55l1.59-4.84c6.53-19.94 10.11-29.82 14.77-39.56 6.07-12.72 12.55-21.18 20.27-25.54 6.66-3.76 10.2-7.86 12.22-13.15a46.6 46.6 0 0 0 1.86-6.58c1.23-5.2 2.05-7.59 3.93-10.36 2.45-3.62 6.27-6.53 12.1-8.96 15.78-6.58 16.73-7.04 18.05-9.01.65-.98.83-2.15.74-4.51-.03-.73-.23-3.82-.24-4A93.8 93.8 0 0 1 119 94c0-10.04.18-11.37 2.37-13.15.52-.42 1.13-.8 2.07-1.3.27-.14 2.18-1.12 2.84-1.48a68.4 68.4 0 0 0 9.12-5.87c2.06-1.54 2.64-2.14 8.01-7.93 3.78-4.09 6.21-6.36 8.96-8.12 3.64-2.33 7.2-3.12 10.9-2.11 4.4 1.2 10.81 2 18.78 2.46 6.9.4 12.9.5 21.95.5 4.87 0 8.97.47 15.4 1.57 7.77 1.33 9.3 1.54 12.38 1.54 4.05 0 7.43-.88 10.68-2.95 5.06-3.22 8.11-4.67 11.2-5.2 3.62-.64 4.77-.46 16.55 2.06 17.26 3.7 30.85 1.36 41.06-9.7 5.1-5.53 5.48-8.9 3.48-14.8-.83-2.42-1.03-3.1-1.17-4.3-.29-2.52.5-4.71 2.71-6.93 2.65-2.65 4.72-9.17 6.22-18.29h2.03c-1.56 9.71-3.77 16.65-6.83 19.7-1.79 1.8-2.36 3.39-2.14 5.28.11 1 .3 1.63 1.07 3.9 2.22 6.53 1.76 10.66-3.9 16.8-10.77 11.66-25.07 14.13-42.95 10.3-11.42-2.45-12.55-2.62-15.78-2.06-2.77.48-5.62 1.84-10.47 4.92a20.93 20.93 0 0 1-11.76 3.27c-3.25 0-4.81-.22-12.73-1.57C212.74 59.46 208.73 59 204 59c-9.1 0-15.11-.1-22.07-.5-8.09-.47-14.62-1.29-19.2-2.54-5.62-1.53-10.17 1.38-17.85 9.66-5.5 5.94-6.08 6.53-8.28 8.18a70.38 70.38 0 0 1-9.38 6.03c-.68.37-2.58 1.35-2.84 1.49-.84.44-1.35.76-1.75 1.08C121.16 83.6 121 84.8 121 94c0 1.85.06 3.54.17 5.44 0 .17.2 3.28.24 4.03.1 2.75-.13 4.29-1.08 5.71-1.67 2.5-2.27 2.8-18.95 9.74-5.48 2.29-8.99 4.96-11.2 8.24-1.71 2.51-2.47 4.73-3.64 9.7-.83 3.5-1.21 4.92-1.94 6.83-2.18 5.73-6.05 10.19-13.1 14.18-7.3 4.12-13.55 12.28-19.46 24.66-4.6 9.64-8.17 19.46-14.67 39.32l-1.58 4.84c-4.75 14.47-7.54 22.48-10.86 30.69-5.28 13.01-13.95 25.65-24.93 37.6v-2.97zm0 78v-.5l1-.01c6.32 0 7.47 5.2 4.6 13.36a60.36 60.36 0 0 1-5.6 11.3v-1.92a57.76 57.76 0 0 0 4.65-9.72c2.69-7.6 1.71-12.02-3.65-12.02-.34 0-.67 0-1 .02v-46.59a340.96 340.96 0 0 0 13.71-8.34c13.66-9.46 29.79-37.6 29.79-53.59 0-18.1 21.57-72.64 32.23-79.42 12.71-8.09 32.24-27.96 35.8-37.75 1.93-5.3 5.5-7.27 14.42-9.37 6.15-1.44 8.64-2.42 10.67-4.79 1.5-1.74 2.72-4.79 4.33-10.3.23-.78 1.9-6.68 2.43-8.46 3.62-12.08 7.3-18.49 13.47-20.39 2.5-.76 3.03-.98 9.74-3.7 7.49-3.03 11.97-4.43 17.12-4.92 6.75-.65 13.13.75 19.55 4.67 5.43 3.32 12.19 4.72 20.17 4.56 6.03-.12 12.2-1.07 19.83-2.8 1.82-.4 7.38-1.74 8.26-1.94 2.69-.6 4.34-.89 5.48-.89 4.97 0 8.93-.05 14.2-.27 7.9-.32 15.56-.92 22.75-1.88 8.5-1.14 15.9-2.73 21.88-4.82 18.9-6.62 32.64-18.3 33.67-27.59.29-2.56.4-2.96 2.79-11.11 2.33-7.95 3.21-12.93 2.72-18.23-.2-2.24-.69-4.38-1.48-6.42-1.5-3.92-2.63-9.4-3.43-16.18h.9c.77 6.47 1.89 11.72 3.47 15.82a24.93 24.93 0 0 1 1.54 6.69c.5 5.46-.4 10.54-2.77 18.6-2.36 8.06-2.47 8.47-2.74 10.95-1.09 9.75-15.1 21.68-34.33 28.41-6.06 2.12-13.52 3.72-22.09 4.87-7.22.96-14.92 1.57-22.83 1.89-5.3.21-9.27.27-14.25.27-1.04 0-2.64.27-5.26.87-.87.2-6.43 1.53-8.26 1.94-7.68 1.73-13.92 2.7-20.03 2.82-8.15.17-15.1-1.27-20.71-4.7-6.23-3.81-12.4-5.16-18.93-4.54-5.04.48-9.44 1.86-16.84 4.86-6.75 2.74-7.29 2.95-9.82 3.73-5.73 1.76-9.28 7.96-12.81 19.72-.53 1.77-2.2 7.66-2.43 8.46-1.66 5.65-2.91 8.78-4.53 10.67-2.22 2.58-4.84 3.62-12.01 5.3-7.8 1.83-11.13 3.66-12.9 8.54-3.65 10.04-23.32 30.06-36.2 38.25C65.94 190 44.5 244.2 44.5 262c0 16.34-16.3 44.78-30.22 54.41-2.14 1.48-8.24 5.12-14.28 8.68v-1.16 46.09zm0-173.7v-1.11c7.42-3.82 14.55-10.23 21.84-18.98 3.8-4.56 14.21-18.78 15.79-20.55 1.8-2.04 4.06-3.96 7.42-6.45 1.08-.8 4.92-3.57 5.49-3.99 9.36-6.85 14-11.96 15.98-19.36.8-2.98 1.54-6.78 2.46-12.3.23-1.44 2-12.46 2.56-15.79 2.87-16.77 5.73-26.79 10.07-32.1C92.46 52.43 101.5 38.13 101.5 33c0-2.54.34-3.35 6.05-15.71.68-1.49 1.25-2.74 1.77-3.93 2.5-5.75 3.9-10.04 4.14-13.36h1c-.23 3.48-1.66 7.87-4.23 13.76-.52 1.2-1.09 2.45-1.78 3.95-5.54 12.01-5.95 12.99-5.95 15.29 0 5.47-9.09 19.84-20.11 33.31-4.2 5.12-7.03 15.06-9.86 31.64-.57 3.33-2.33 14.33-2.57 15.78-.92 5.56-1.67 9.38-2.48 12.4-2.05 7.68-6.82 12.93-16.35 19.91l-5.49 3.98c-3.3 2.45-5.51 4.34-7.27 6.31-1.53 1.73-11.94 15.93-15.76 20.53-7.52 9.02-14.88 15.6-22.61 19.46zm0 361.83v-4.33c.48 2.36 1 4.35 1.6 6.15 2 6.03 4.6 8.26 8.19 6.59C28.76 557.69 43.5 542.4 43.5 527c0-16.2 6.37-31.99 17.1-46.3 1.88-2.5 3.66-4.4 5.53-6 .73-.62 1.45-1.18 2.3-1.8l2-1.43c3.68-2.68 5.32-5.28 7.08-12.59.75-3.07 1.38-5.02 4.2-13.26l.63-1.88c3.24-9.58 4.56-14.97 4.17-18.65-.48-4.43-3.8-5.23-11.3-1.64a81.12 81.12 0 0 1-9.15 3.7c-13.89 4.67-26.96 5.8-42.66 5.42l-1.95-.05-1.45-.02a39.8 39.8 0 0 0-15.05 2.96A21.81 21.81 0 0 0 0 438.37v-1.26a23.55 23.55 0 0 1 4.55-2.57 40.77 40.77 0 0 1 16.92-3.02l1.95.05c15.6.38 28.57-.75 42.32-5.37a80.12 80.12 0 0 0 9.04-3.65c8.04-3.84 12.16-2.85 12.72 2.43.42 3.89-.92 9.34-4.21 19.08l-.64 1.88c-2.8 8.2-3.43 10.15-4.16 13.18-1.82 7.52-3.59 10.34-7.47 13.16l-2 1.43c-.84.6-1.54 1.15-2.25 1.75a35.45 35.45 0 0 0-5.37 5.84c-10.61 14.15-16.9 29.74-16.9 45.7 0 15.88-15 31.45-34.29 40.45-4.3 2.01-7.39-.66-9.56-7.18-.23-.68-.44-1.39-.65-2.13zm0-62.16v-2.45l1.46 3.27c2.1 4.8 3.46 10.33 4.26 16.77.66 5.3.84 9.3 1.04 18.5.2 9.32.5 12.75 1.63 15.05 1.28 2.6 3.67 2.35 8.29-1.5 17.14-14.3 21.82-22.9 21.82-38.62 0-7.17 1.1-12.39 3.7-17.68 2.27-4.67 3.65-6.62 13.4-19.62a69.8 69.8 0 0 1 7.6-8.79 44.76 44.76 0 0 1 3.54-3.06c.38-.3.64-.52.89-.74a10.47 10.47 0 0 0 2.63-3.32 35.78 35.78 0 0 0 2.26-5.94l.37-1.2.36-1.15c.29-.91.48-1.55.66-2.16.45-1.53.74-2.68.91-3.66.38-2.2.12-3.49-.85-4.15-2.35-1.61-9.28-.24-23.8 4.94-9.54 3.4-16.12 4.17-27.85 4.26-7.71.06-10.43.4-13.25 2.12-3.48 2.12-5.84 6.4-7.58 14.26-.5 2.2-.99 4.19-1.49 5.98v-3.98l.51-2.22c1.8-8.1 4.28-12.6 8.04-14.9 3.04-1.85 5.86-2.2 13.77-2.26 11.61-.09 18.1-.84 27.51-4.2 14.93-5.32 21.95-6.71 24.7-4.83 1.38.94 1.71 2.6 1.28 5.15a33.69 33.69 0 0 1-.94 3.78l-.66 2.17-.36 1.15-.37 1.2a36.64 36.64 0 0 1-2.33 6.1c-.8 1.53-1.61 2.52-2.86 3.61l-.92.77-1.02.83c-.9.74-1.65 1.4-2.47 2.18a68.84 68.84 0 0 0-7.48 8.66c-9.7 12.93-11.07 14.87-13.31 19.46-2.52 5.15-3.59 10.22-3.59 17.24 0 16.04-4.82 24.91-22.18 39.38-5.04 4.2-8.18 4.55-9.83 1.18-1.22-2.5-1.52-5.94-1.73-15.47-.2-9.16-.38-13.15-1.03-18.4-.79-6.34-2.12-11.8-4.19-16.49L0 495.98zM379.27 0h1.04l1.5 5.26c3.28 11.56 4.89 19.33 5.26 27.8.49 11.01-1.52 21.26-6.63 31.17-7.8 15.13-20.47 26.5-36.22 34.1-12.38 5.96-26.12 9.17-36.22 9.17-6.84 0-17.24 1.38-37.27 4.62l-2.27.37c-24.5 3.99-31.65 5-37.46 5-3.49 0-4.08-.08-19.54-2.8-3.56-.64-6.32-1.1-9-1.5-20.23-2.96-31-1.2-31.96 7.86-.1.85-.18 1.72-.29 2.81l-.27 2.73c-1.1 10.9-2.02 15.73-4.31 19.96-2.9 5.34-7.77 7.95-15.63 7.95-10.2 0-12.92.6-15.5 3.17.52-.51-5.03 5.85-8.16 8.7-2.75 2.5-14.32 12.55-15.77 13.83a341.27 341.27 0 0 0-6.54 5.92c-6.97 6.49-11.81 11.76-14.6 16.15-5.92 9.3-10.48 18.04-11.69 24.08-1.66 8.3 3.67 9.54 19.02 1.21a626.23 626.23 0 0 1 44.54-21.9c3.5-1.56 14.04-6.2 15.68-6.95 5.05-2.25 8.3-3.8 10.78-5.15l1.95-1.07 2.18-1.18c1.76-.94 3.38-1.76 5-2.55 18.1-8.72 34.48-10.46 50.33-1.2 22.89 13.34 38.28 37.02 38.28 56.44 0 19.12-.73 25.13-5.18 33.2a45.32 45.32 0 0 1-4.94 7.12c-6.47 7.77-11.81 16.2-12.76 21.27-1.2 6.34 4.69 7.03 20.17-.05 13.31-6.08 22.4-14.95 28.5-26.32a80.51 80.51 0 0 0 6.1-15.13c.9-2.98 3.17-11.65 3.41-12.48a29.02 29.02 0 0 1 1.75-4.83c7.47-14.93 21.09-30.5 36.25-37.24 7.61-3.38 13-9.65 19.4-20.79.84-1.48 4.26-7.64 5.14-9.17 3.52-6.1 6.22-9.7 9.37-11.98 10.15-7.4 28.7-11.1 50.29-11.1 7.52 0 16.54-1.24 27.51-3.58a420.1 420.1 0 0 0 14.96-3.52c-1.3.33 15.54-3.98 19.42-4.89 14.15-3.33 41.07-5.01 64.11-5.01 17.36 0 27.82-9.23 38.53-38.67 6.62-18.21 6.62-26.37 2.69-34.35l-1.18-2.37A13.36 13.36 0 0 1 587.5 58c0-4.03 0-4.01 2.5-24.56.46-3.73.8-6.74 1.12-9.64.9-8.45 1.38-15.2 1.38-20.8 0-.94-.02-1.94-.04-3h1c.03 1.06.04 2.06.04 3 0 5.65-.48 12.43-1.39 20.9-.3 2.91-.66 5.93-1.11 9.66-2.5 20.45-2.5 20.47-2.5 24.44 0 1.97.45 3.57 1.45 5.68.24.51 1.16 2.35 1.17 2.36 4.06 8.24 4.06 16.68-2.65 35.13-10.84 29.8-21.63 39.33-39.47 39.33-22.96 0-49.83 1.68-63.89 4.99-3.86.9-20.69 5.2-19.4 4.88a421.05 421.05 0 0 1-14.99 3.53c-11.04 2.35-20.11 3.6-27.72 3.6-21.4 0-39.76 3.67-49.7 10.9-3 2.19-5.64 5.7-9.1 11.68-.87 1.52-4.29 7.68-5.14 9.17-6.49 11.3-12 17.71-19.86 21.2-14.9 6.63-28.38 22.03-35.75 36.77a28.17 28.17 0 0 0-1.69 4.67c-.23.8-2.5 9.49-3.4 12.5a81.48 81.48 0 0 1-6.19 15.3c-6.2 11.56-15.44 20.58-28.96 26.76-16.1 7.36-23 6.55-21.58-1.04 1-5.29 6.4-13.83 12.99-21.73a44.33 44.33 0 0 0 4.82-6.96c4.35-7.88 5.06-13.77 5.06-32.72 0-19.04-15.19-42.4-37.72-55.55-15.57-9.08-31.62-7.38-49.45 1.21a132.9 132.9 0 0 0-7.14 3.71l-1.95 1.07a158.83 158.83 0 0 1-10.85 5.19c-1.65.74-12.18 5.38-15.69 6.95a625.25 625.25 0 0 0-44.46 21.86c-15.95 8.66-22.37 7.16-20.48-2.29 1.24-6.2 5.83-15.02 11.82-24.42 2.85-4.48 7.74-9.8 14.77-16.34 1.98-1.85 4.12-3.79 6.56-5.94 1.46-1.29 13.02-11.33 15.75-13.82 3.09-2.8 8.6-9.14 8.14-8.67 2.82-2.82 5.75-3.46 16.2-3.46 7.5 0 12.04-2.43 14.75-7.42 2.2-4.07 3.11-8.84 4.2-19.59l.26-2.73.3-2.81c.56-5.42 4.47-8.5 11.23-9.6 5.44-.88 12.51-.51 21.86.86 2.7.4 5.47.86 9.04 1.49 15.33 2.7 15.96 2.8 19.36 2.8 5.73 0 12.9-1.03 37.3-5l2.27-.36c20.1-3.26 30.52-4.64 37.43-4.64 9.95 0 23.54-3.18 35.78-9.08 15.57-7.5 28.09-18.73 35.78-33.65 5.02-9.75 7-19.82 6.51-30.67-.37-8.37-1.96-16.08-5.23-27.57L379.27 0zm13.68 0h1.02c.78 3.9 1.92 8.7 3.51 14.88 3.63 14.05 3.06 27.03-.75 38.77a61 61 0 0 1-11.35 20.68 138.36 138.36 0 0 1-19.32 18.77c-11.32 9.02-23.36 15.49-35.95 18.39a258.63 258.63 0 0 1-22.57 4.07c-3.17.44-6.36.85-10.3 1.32l-9.39 1.12c-11.53 1.41-17.45 2.55-21.64 4.46-9.28 4.21-28.35 6.04-49.21 6.04-1.37 0-2.8-.12-4.3-.35-2.62-.41-5-1.03-9.14-2.29-7.34-2.21-9.63-2.75-12.63-2.56-3.9.23-6.63 2.29-8.47 6.89-1.86 4.66-2.42 7.53-3.34 14.98-1.1 8.98-2.87 12.12-9.97 14.3a40.12 40.12 0 0 0-6.8 2.66c-.63.33-1.16.64-1.76 1.02l-1.34.86c-1.9 1.14-3.86 1.49-9.25 1.49-3.2 0-8.83-.55-9.51-.39-1.22.28-.75-.14-7.14 6.24-1.5 1.5-3.49 3.18-6.32 5.37-1.52 1.18-7.16 5.43-7.94 6.03-4.96 3.78-8.33 6.6-11.06 9.38-4.88 4.98-6.85 9.15-5.56 12.7 1.34 3.67 4.07 4.42 8.9 2.82a55.72 55.72 0 0 0 7.77-3.48c1.5-.77 7.78-4.13 9.37-4.96a116.8 116.8 0 0 1 12.31-5.68 162.2 162.2 0 0 0 11.04-4.84c2.04-.97 10.74-5.16 13-6.22 4.41-2.1 8.1-3.78 11.65-5.29 17.14-7.3 29.32-9.9 37.67-6.65l5.43 2.1c2.3.88 4.17 1.62 6.02 2.38a150.9 150.9 0 0 1 13.07 6c18.34 9.63 30.35 22.13 34.79 39.87 6.96 27.85 3.6 45.53-8.08 62.4-3.97 5.75-3.52 9.2.06 8.97 4.14-.28 10.21-4.95 15.11-12.52 3.1-4.8 5.1-10.45 8.05-21.53l1.69-6.35c.66-2.47 1.24-4.52 1.83-6.5 4.93-16.56 11-27.28 21.56-34.76 7.15-5.06 23.73-15.5 25.48-16.75 6.74-4.81 10.53-9.44 14.34-18 7.74-17.44 21.09-24.34 44.47-24.34 9.36 0 17.91-1.13 29.53-3.49a624.86 624.86 0 0 0 6.2-1.28c2.4-.5 4.07-.84 5.66-1.13 4.03-.74 7.04-1.1 9.61-1.1 4.44 0 9.39-1 31.39-5.99l2.95-.66c16.34-3.67 25.64-5.35 31.66-5.35 1.54 0 2.4.01 6.4.1 7.8.15 12.27.13 17.33-.2 16.41-1.06 26.73-5.36 29.8-14.56a87.1 87.1 0 0 1 3.55-8.83c-.15.31 2.29-4.96 2.9-6.38 5.38-12.3 5.57-21.92-1.44-39.44a86.4 86.4 0 0 1-5.26-20.72c-1.61-11.98-1.38-23.14.1-40.35l.2-2.12h1l-.2 2.2c-1.48 17.15-1.7 28.24-.11 40.14a85.4 85.4 0 0 0 5.2 20.47c7.1 17.78 6.91 27.67 1.43 40.22-.62 1.43-3.06 6.72-2.91 6.4a86.17 86.17 0 0 0-3.52 8.73c-3.23 9.72-13.9 14.15-30.68 15.24-5.1.33-9.58.35-17.42.2-3.98-.09-4.84-.1-6.37-.1-5.91 0-15.18 1.67-31.44 5.32l-2.95.67c-22.16 5.02-27.05 6.01-31.61 6.01-2.5 0-5.45.36-9.43 1.09-1.58.29-3.25.62-5.64 1.11a4894.21 4894.21 0 0 0-6.2 1.29c-11.68 2.37-20.3 3.51-29.73 3.51-23.02 0-36 6.71-43.53 23.66-3.9 8.8-7.82 13.58-14.7 18.5-1.78 1.27-18.36 11.7-25.48 16.75-10.34 7.32-16.3 17.87-21.19 34.23-.58 1.96-1.15 4-1.82 6.47l-1.69 6.35c-2.98 11.18-5 16.9-8.17 21.81-5.05 7.81-11.37 12.68-15.89 12.98-4.7.31-5.3-4.23-.94-10.53 11.52-16.64 14.82-34.03 7.92-61.6-4.35-17.42-16.16-29.72-34.27-39.22-4-2.1-8.2-4-12.99-5.97-1.84-.75-3.7-1.49-6-2.38l-5.43-2.08c-8.03-3.12-20.02-.58-36.92 6.63-3.52 1.5-7.21 3.19-11.61 5.27l-13 6.22c-4.71 2.22-8.16 3.75-11.11 4.88a115.87 115.87 0 0 0-12.21 5.63c-1.58.83-7.86 4.18-9.37 4.96a56.55 56.55 0 0 1-7.9 3.54c-5.3 1.75-8.62.85-10.17-3.43-1.46-4.02.66-8.5 5.8-13.74 2.75-2.82 6.16-5.66 11.15-9.48.79-.6 6.43-4.85 7.94-6.02a66.96 66.96 0 0 0 6.23-5.28c6.74-6.74 6.1-6.16 7.61-6.51.87-.2 6.69.36 9.74.36 5.22 0 7.03-.32 8.74-1.35l1.31-.84c.62-.4 1.18-.72 1.84-1.07a41.07 41.07 0 0 1 6.96-2.72c6.64-2.04 8.22-4.84 9.28-13.47.93-7.53 1.5-10.47 3.4-15.24 1.99-4.95 5.04-7.26 9.34-7.51 3.17-.2 5.5.35 12.97 2.6a63.54 63.54 0 0 0 9.02 2.26c1.45.22 2.83.34 4.14.34 20.71 0 39.7-1.82 48.8-5.96 4.32-1.96 10.29-3.1 21.93-4.53l9.4-1.12c3.92-.48 7.11-.88 10.27-1.32 8.16-1.14 15.4-2.43 22.49-4.06 12.42-2.86 24.33-9.26 35.55-18.2a137.4 137.4 0 0 0 19.18-18.64 60.02 60.02 0 0 0 11.15-20.32c3.76-11.57 4.32-24.36.75-38.23A284.86 284.86 0 0 1 392.95 0zM506.7 0h1.26c-.5.66-.9 1.18-1.17 1.51-3.95 4.96-6.9 7.92-9.82 9.57A10.02 10.02 0 0 1 492 12.5c-2.38 0-4.24.67-6.71 2.21l-2.65 1.71c-4.38 2.8-8.01 4.08-13.64 4.08-5.6 0-9.99-1.26-16.08-4.05a202.63 202.63 0 0 1-2.3-1.06l-2.18-.98c-1.6-.7-2.92-1.17-4.17-1.48a13.42 13.42 0 0 0-3.27-.43c-2.3 0-4.3-.68-11-3.37l-1.56-.62c-5-1.97-8.1-2.82-10.52-2.66-2.93.2-4.42 2.03-4.42 6.15 0 20.76-5.21 50.42-12.15 57.35-7.58 7.59-26.55 23.7-34.06 29.06-13.16 9.4-31.17 20.2-44.11 25.06a106.87 106.87 0 0 1-13.32 4.03c-3.28.78-6.6 1.43-11.25 2.24-.53.1-8.8 1.5-11.5 1.99-4.86.87-9.3 1.74-14 2.76-20.62 4.48-25.07 5.01-38.11 5.01-2.49 0-2.9-.07-14.05-2-2.42-.42-4.31-.73-6.15-1-8.11-1.19-13.83-1.36-17.64-.2-4.54 1.4-5.93 4.65-3.7 10.52 2.02 5.28 4.84 8.61 8.84 10.74 3.26 1.74 6.75 2.6 13.82 3.71 9.42 1.48 10.94 1.75 15.5 2.92a78.2 78.2 0 0 1 18.62 7.37c8.3 4.58 14.58 11.5 19.98 20.89 2.73 4.73 9.46 19.33 10.54 21.19 3.4 5.85 6.26 6.63 10.89 2 4.95-4.94 10.35-8.37 21.13-14.06.47-.25 2.06-1.1 2.12-1.12 7.98-4.21 11.92-6.51 15.87-9.54 5.11-3.9 8.66-8.1 10.77-13.11 8.52-20.24 20.75-33.31 32.46-33.31l5.5.03c10.53.08 17.35.02 24.9-.31 13.66-.62 23.78-2.09 29.39-4.67 5.85-2.7 13.42-5.49 24.18-9.02 3.46-1.14 6.29-2.05 12.7-4.1 7.7-2.45 11.08-3.54 15.17-4.9a1059.43 1059.43 0 0 1 11.33-3.72c3.67-1.2 5.96-2 8.03-2.78a59.88 59.88 0 0 0 6.66-2.94c1.87-.98 3.76-2.1 5.86-3.5 3.48-2.33 6.15-3.13 12.04-4.13l1.15-.2c5.71-1.01 9-2.3 12.76-5.63 7.82-6.96 8.58-23.18 3.84-44.52-1.7-7.67-2.1-19.28-1.57-35.47A837.22 837.22 0 0 1 546.76 0h1l-.15 3.06c-.32 6.42-.53 11.02-.68 15.62-.51 16.1-.12 27.65 1.56 35.21 4.82 21.68 4.04 38.2-4.16 45.48-3.91 3.48-7.37 4.84-13.24 5.87l-1.16.2c-5.76.99-8.32 1.75-11.65 3.98a63.73 63.73 0 0 1-5.96 3.56 60.86 60.86 0 0 1-6.77 2.99c-2.09.79-4.39 1.58-8.07 2.79a5398.31 5398.31 0 0 1-11.32 3.71c-4.1 1.37-7.48 2.46-15.18 4.92-6.42 2.04-9.24 2.95-12.7 4.08-10.73 3.53-18.27 6.3-24.07 8.98-5.76 2.66-15.97 4.14-29.77 4.77-7.56.33-14.4.39-24.95.31l-5.49-.03c-11.19 0-23.16 12.79-31.54 32.7-2.19 5.19-5.84 9.52-11.08 13.52-4.02 3.07-7.99 5.39-16.01 9.62l-2.12 1.12c-10.7 5.65-16.04 9.04-20.9 13.9-5.14 5.14-8.75 4.15-12.45-2.22-1.12-1.92-7.85-16.5-10.54-21.2-5.33-9.24-11.48-16.02-19.6-20.5a77.2 77.2 0 0 0-18.4-7.28c-4.5-1.17-6.02-1.43-15.4-2.9-7.17-1.12-10.74-2-14.13-3.81-4.22-2.25-7.2-5.77-9.3-11.27-2.43-6.39-.78-10.26 4.34-11.83 4-1.22 9.82-1.05 18.08.17 1.84.27 3.74.58 6.17 1 11.02 1.9 11.48 1.98 13.88 1.98 12.96 0 17.35-.52 37.9-4.99 4.71-1.02 9.16-1.9 14.03-2.77 2.71-.48 10.98-1.9 11.5-1.98 4.64-.81 7.95-1.46 11.2-2.23 4.55-1.07 8.76-2.34 13.2-4 12.83-4.81 30.79-15.59 43.88-24.94 7.47-5.33 26.4-21.4 33.94-28.94C407.3 61.98 412.5 32.49 412.5 12c0-4.61 1.86-6.9 5.35-7.15 2.63-.18 5.8.7 10.96 2.73l1.56.62c6.53 2.62 8.53 3.3 10.63 3.3 1.14 0 2.3.16 3.5.46 1.32.33 2.68.82 4.34 1.53a90.97 90.97 0 0 1 3.34 1.52l1.15.54c5.98 2.73 10.23 3.95 15.67 3.95 5.41 0 8.87-1.21 13.1-3.92.2-.13 2.1-1.38 2.66-1.72 2.62-1.63 4.64-2.36 7.24-2.36 1.47 0 2.94-.43 4.47-1.3 2.78-1.56 5.67-4.45 9.54-9.31l.7-.89zM324.54 600h-2.03c.49-2.96.91-6.2 1.28-9.66.44-4.1.76-8.25.98-12.21.08-1.39.14-2.65-.35-7.29-.47-1.94-.93-4.14-1.36-6.54-2.01-11.26-2.66-22.9-1.14-33.78a60.76 60.76 0 0 1 5.18-17.95 70.78 70.78 0 0 1 12.6-18.22c3.38-3.6 5.53-5.5 11.83-10.79 4.5-3.78 6.35-5.56 7.52-7.5.64-1.07.95-2.06.95-3.06 0-1.75 0-1.74-.75-9.23-.36-3.7-.57-6.3-.68-8.96-.5-12.1 1.62-19.6 8.11-21.76 15.9-5.3 25.89-12.1 33.45-25.54C409.6 390.65 425.85 376 436 376c12.36 0 20-1.96 29.41-8.8 6.76-4.92 9.5-6.6 12.47-7.46 2.22-.64 3.8-.74 9.12-.74 1.86 0 3.53-.83 5.57-2.62 1.08-.96 5.11-5.12 5.6-5.6 6.04-5.85 11.98-8.78 20.83-8.78 2.45 0 4.54.04 7.32.12 7.51.23 8.87.17 11.27-.7 3.03-1.1 5.53-3.03 14.75-11.17 8-7.06 10.72-8.92 22.87-16.47 1.44-.9 2.59-1.63 3.69-2.37a69.45 69.45 0 0 0 9.46-7.5c4.12-3.88 8.02-7.85 11.64-11.9v2.98a201.58 201.58 0 0 1-10.27 10.38c-3.18 3-6.2 5.35-9.72 7.7-1.12.76-2.28 1.5-3.75 2.4-12.05 7.5-14.71 9.32-22.6 16.28-9.46 8.35-12.01 10.32-15.39 11.55-2.74 1-4.19 1.06-12.01.82-2.76-.08-4.83-.12-7.26-.12-8.27 0-13.75 2.7-19.43 8.22-.44.43-4.52 4.64-5.68 5.66-2.37 2.09-4.46 3.12-6.89 3.12-5.1 0-6.6.1-8.56.66-2.67.78-5.29 2.37-11.85 7.15-9.8 7.13-17.85 9.19-30.59 9.19-9.22 0-24.96 14.2-34.13 30.49-7.84 13.94-18.24 21.02-34.55 26.46-5.31 1.77-7.21 8.51-6.75 19.78.1 2.6.31 5.19.68 8.84.75 7.62.75 7.58.75 9.43 0 1.38-.42 2.73-1.24 4.09-1.33 2.2-3.26 4.07-7.94 8-6.25 5.24-8.36 7.12-11.67 10.63a68.8 68.8 0 0 0-12.25 17.71 58.8 58.8 0 0 0-5 17.36c-1.49 10.66-.85 22.09 1.13 33.15.43 2.37.88 4.53 1.33 6.44.16.66.3 1.25.6 4.06a249.3 249.3 0 0 1-1.17 16.12c-.37 3.37-.78 6.53-1.25 9.44zm-13.4 0h-1.05l.12-.28c3.07-7.16 4.29-11.83 4.29-18.72 0-3.57-.07-4.93-.76-15.65-.77-12.04-1-19.64-.55-28.3.58-11.5 2.4-22.1 5.81-32.16 1.3-3.8 2.8-7.5 4.55-11.1 3.46-7.14 6.83-12.39 10.42-16.6a59.02 59.02 0 0 1 4.35-4.56c.43-.4 3-2.8 3.67-3.45 5.72-5.6 7.51-11.52 7.51-29.18 0-18.84 2.9-23.77 15.82-28.24 1.09-.37 1.92-.67 2.77-.98a51.3 51.3 0 0 0 6.1-2.7c4.95-2.6 9.64-6.22 14.44-11.42 25.5-27.63 37.15-35.16 56.37-35.16 8.28 0 14.54-1.95 22-6.3 1.78-1.03 13.82-8.82 18.16-11.27 2.83-1.59 5.66-3.03 8.63-4.39 7.92-3.6 13.97-4.45 26.6-4.8 7.53-.2 10.7-.49 14.26-1.58 4.55-1.4 8.06-4 10.93-8.43 2.2-3.41 6.85-7.08 14.66-12.06 1.61-1.03 3.27-2.05 5.65-3.5 9.53-5.85 11.56-7.13 14.81-9.57 5.34-4 9.3-8.37 13.68-14.77a204.2 204.2 0 0 0 5.62-8.75v1.9c-1.97 3.17-3.4 5.38-4.8 7.42-4.42 6.48-8.46 10.92-13.9 15-3.29 2.46-5.32 3.75-14.89 9.61a375.06 375.06 0 0 0-5.63 3.5c-7.7 4.9-12.26 8.52-14.36 11.76-3 4.63-6.7 7.39-11.48 8.85-3.68 1.12-6.9 1.42-14.53 1.63-12.5.34-18.44 1.18-26.2 4.7a111.08 111.08 0 0 0-8.56 4.35c-4.3 2.43-16.34 10.22-18.15 11.27-7.6 4.43-14.03 6.43-22.5 6.43-18.87 0-30.3 7.4-55.63 34.84-4.88 5.28-9.67 8.97-14.7 11.62-2 1.05-4 1.92-6.23 2.75-.86.32-1.7.62-5.37 1.87-5.08 1.76-7.44 3.25-9.28 6.37-2.23 3.78-3.29 9.94-3.29 20.05 0 17.9-1.87 24.07-7.8 29.89-.69.67-3.27 3.06-3.69 3.46a58.04 58.04 0 0 0-4.28 4.49c-3.53 4.14-6.86 9.32-10.28 16.38a95.19 95.19 0 0 0-4.5 10.99c-3.38 9.97-5.18 20.48-5.76 31.9-.44 8.6-.22 16.17.55 28.17.69 10.76.76 12.12.76 15.72 0 6.35-1.02 10.87-4.35 19zm25.08 0h-1c-.04-4.73.06-9.39.28-15.02.26-6.41-.4-11.79-2.53-24.37l-.31-1.86c-2.12-12.55-2.76-19.35-1.97-26.47 1.03-9.25 4.75-16.68 12-22.67 22.04-18.2 29.81-30.18 29.81-44.61 0-2.6-.3-4.81-.98-8.17-.97-4.79-1.1-5.68-.97-7.57.2-2.56 1.27-4.7 3.56-6.72 2.67-2.35 7.05-4.6 13.72-7.01 9.72-3.5 15.52-9.18 24.3-21.57l1.78-2.5c4.48-6.33 7.1-9.63 10.43-12.78 4.31-4.07 8.98-6.77 14.54-8.17 13.3-3.32 20.37-5.47 25.34-7.64a49.5 49.5 0 0 0 5.28-2.7c1.1-.65 1.75-1.04 4.24-2.6 2.7-1.68 5.22-2.08 11.38-2.28 5.44-.18 7.9-.43 10.97-1.41a21.47 21.47 0 0 0 9.54-6.22c4.87-5.3 10.03-7.61 17.79-8.9 1.07-.18 1.88-.3 3.86-.58 6.9-.97 9.94-1.69 13.48-3.62 4.5-2.45 6.79-4.44 23.46-19.68l3.14-2.85c9.65-8.71 16.12-13.83 21.42-16.48 4.25-2.12 7.6-4.69 11.22-8.6v1.45c-3.42 3.57-6.69 6-10.78 8.05-5.18 2.59-11.61 7.67-21.2 16.32l-3.12 2.85c-16.8 15.35-19.05 17.3-23.66 19.82-3.68 2-6.8 2.75-13.82 3.73-1.97.28-2.78.4-3.84.57-7.56 1.26-12.52 3.48-17.21 8.6a22.47 22.47 0 0 1-9.97 6.5c-3.2 1-5.72 1.27-11.25 1.45-5.98.2-8.39.57-10.89 2.13a144 144 0 0 1-4.25 2.61 50.48 50.48 0 0 1-5.39 2.75c-5.04 2.2-12.15 4.37-25.5 7.7-9.74 2.44-15.26 7.65-24.4 20.56l-1.77 2.5c-8.9 12.54-14.82 18.34-24.78 21.93-6.57 2.36-10.85 4.57-13.4 6.82-2.1 1.86-3.05 3.74-3.22 6.04-.13 1.76 0 2.63.95 7.3.7 3.42 1 5.7 1 8.37 0 14.79-7.93 27-30.18 45.39-7.03 5.8-10.64 13-11.64 22-.78 7-.14 13.73 1.96 26.2l.32 1.85c2.15 12.65 2.8 18.07 2.54 24.58-.22 5.57-.32 10.2-.28 14.98zM95.9 600h-2.04c.68-3.82 1.14-8.8 1.61-15.98.2-3.11.27-4.06.39-5.6 1.3-17.54 4.04-27.14 11.5-33.2 4.65-3.77 7.22-8.92 8.67-16 .51-2.52.7-3.87 1.33-9.17.66-5.5 1.16-8.06 2.24-10.36 1.45-3.09 3.82-4.69 7.39-4.69 14.28 0 38.48 9.12 53.6 20.2 8.66 6.35 21.26 13.32 31.74 17.11 13.03 4.71 21.89 4.41 24.75-1.73 1.7-3.64 1.92-4.11 2.65-5.77 2.93-6.67 4.69-12.2 5.25-17.5.23-2.17.24-4.23.02-6.2-.32-2.75-1.42-4.55-4.08-7.35l-1.32-1.37a30.59 30.59 0 0 1-2.41-2.79 30.37 30.37 0 0 1-2.5-4.07l-1.13-2.14c-1.62-3.1-2.68-4.6-4.12-5.56-5.26-3.5-14.8-5.5-28.55-6.83a272.42 272.42 0 0 0-9.04-.71l-2.18-.17c-9.57-.73-15.12-1.56-19.06-3.2C156.57 471.07 136 450.5 136 440c0-5.34 1.74-9.53 5.47-14.13 1.98-2.44 11.12-11.71 12.79-13.54 4.52-4.97 10.16-9.54 17.68-14.66 2.8-1.9 14.78-9.6 17.49-11.49a50.54 50.54 0 0 0 6.34-5.43c1.53-1.5 6.96-7.13 7.12-7.3 7.18-7.3 12.7-11.56 19.74-14.38 3.36-1.34 8.13-2.79 17.45-5.38a9577.18 9577.18 0 0 1 11.78-3.28 602.6 602.6 0 0 0 12.67-3.7c20.4-6.24 34-12.08 40.79-18.44 8.74-8.2 11.78-13.84 15.73-26.02 2.02-6.22 3.09-9.04 5.07-12.72 9.54-17.71 28.71-39.37 43.5-45.45C383.77 238.25 389 232.34 389 226c0-2.89 2.73-8.4 6.83-13.73 4.76-6.2 10.65-11.36 16.75-14.18 12.5-5.77 33.5-10.09 47.42-10.09 5.32 0 9.83-1.5 16.42-4.89 9.2-4.71 10.1-5.11 13.58-5.11 10.42 0 32.06-2.55 45.76-5.97l3.88-.98 3.47-.89c2.6-.66 4.33-1.08 5.93-1.43 3.9-.86 6.76-1.23 9.58-1.17 2.74.06 5.47.52 8.67 1.48 4.56 1.37 13.71-.9 22.87-5.68a68.07 68.07 0 0 0 9.84-6.2v2.4c-11.09 8.14-25.76 13.66-33.29 11.4a29.72 29.72 0 0 0-8.13-1.4c-2.63-.05-5.36.3-9.11 1.12a238 238 0 0 0-9.33 2.3l-3.9.99C522.38 177.43 500.58 180 490 180c-2.99 0-3.91.4-12.67 4.89-6.85 3.51-11.61 5.11-17.33 5.11-13.65 0-34.35 4.26-46.58 9.9-5.78 2.67-11.42 7.62-16 13.58-3.85 5.02-6.42 10.2-6.42 12.52 0 7.27-5.8 13.82-20.62 19.92-14.27 5.88-33.16 27.21-42.5 44.55-1.9 3.55-2.95 6.28-4.93 12.4-4.05 12.47-7.23 18.39-16.27 26.86-7.08 6.64-20.87 12.57-41.57 18.89a604.52 604.52 0 0 1-12.7 3.71 1495.1 1495.1 0 0 1-11.8 3.28c-9.24 2.58-13.97 4.01-17.24 5.32-6.73 2.69-12.05 6.8-19.05 13.92-.15.15-5.6 5.8-7.15 7.32a52.4 52.4 0 0 1-6.6 5.65c-2.74 1.92-14.75 9.63-17.5 11.5-7.4 5.04-12.94 9.52-17.33 14.35-1.72 1.9-10.8 11.11-12.71 13.46-3.47 4.26-5.03 8.03-5.03 12.87 0 9.5 20 29.5 33.38 35.08 3.67 1.53 9.1 2.34 18.45 3.05a586.23 586.23 0 0 0 4.34.32c3.24.23 5.07.37 6.93.55 14.08 1.37 23.82 3.4 29.45 7.17 1.82 1.2 3.02 2.91 4.8 6.29l1.11 2.13a28.55 28.55 0 0 0 2.34 3.81c.62.83 1.3 1.6 2.26 2.61.23.24 1.1 1.16 1.32 1.37 2.93 3.09 4.24 5.23 4.61 8.5.24 2.12.23 4.33-.01 6.64-.59 5.55-2.4 11.25-5.41 18.1-.74 1.67-.96 2.15-2.66 5.8-3.49 7.47-13.33 7.8-27.25 2.77-10.67-3.86-23.43-10.92-32.25-17.38C164.62 515.96 140.82 507 127 507c-5 0-6.4 3.02-7.64 13.29a99.03 99.03 0 0 1-1.36 9.33c-1.53 7.5-4.3 13.04-9.37 17.16-6.87 5.58-9.5 14.78-10.77 31.8-.11 1.52-.18 2.47-.38 5.57-.46 7.01-.91 11.99-1.57 15.85zm8.05 0h-1.02c.29-1.41.58-2.94.9-4.59l1.05-5.62c2.5-13.3 4.2-19.92 6.68-24.05 1.7-2.84 3.68-5.5 8.05-11.03 8.21-10.36 10.88-14.55 10.88-18.71l-.02-1.69c-.02-1.78-.02-2.7.02-3.77.21-5.05 1.47-8.2 4.64-9.4 3.92-1.5 10.39.44 20.12 6.43 9.56 5.88 17.53 10.7 25.91 15.66 1.31.78 14.27 8.41 17.67 10.45a714.21 714.21 0 0 1 6.42 3.9c13.82 8.5 38.94 5.05 46.3-7.83 3.6-6.28 4.54-8.52 7.78-17.32a82.3 82.3 0 0 1 1.18-3.07 42.27 42.27 0 0 1 4.06-7.64c9.33-13.98 14.92-26.1 14.92-36.72 0-3.66.75-6.62 3.36-14.85.52-1.64.83-2.66 1.15-3.73 3.64-12.23 3.04-19.12-4.29-24a23.1 23.1 0 0 0-9.98-3.78c-7.2-.93-14.49 1.17-23.91 5.88-1.55.78-6.64 3.44-7.6 3.93a62.6 62.6 0 0 0-4.14 2.3l-4.4 2.66c-11.62 6.92-20.4 9.18-32.81 6.08-3.32-.84-6.24-1.4-13.1-2.64-13.25-2.39-18.7-3.75-23.33-6.46-6.23-3.67-7.46-9.02-2.88-16.65A93.1 93.1 0 0 1 172 415.42a157 157 0 0 1 8.32-7.66c-.07.05 6.16-5.3 7.82-6.77a85.12 85.12 0 0 0 6.5-6.33c7.7-8.46 12.78-13.36 20.08-18.57 9.94-7.1 21.4-12.36 35.18-15.58 37.03-8.64 51-12.7 58.83-17.93 8.6-5.73 21.3-24.77 36.84-54.81 5.22-10.1 12.27-18.4 21.13-25.71 5.13-4.24 9.56-7.25 17.55-12.23 7.42-4.62 9.62-6.14 11.38-8.16a21.15 21.15 0 0 0 2.95-4.87c.61-1.3 2.87-6.47 3-6.77 1.36-3 2.56-5.4 3.95-7.73 6.53-10.97 16.03-18 31.4-20.8 12.73-2.3 19.85-2.7 29.68-2.3 3.25.13 4.13.16 5.6.14 5.15-.07 9.71-1.04 16.61-3.8 20.74-8.3 38.75-12.04 59.19-12.04 3.05 0 6.03.15 10.48.48l2.09.16c12.45.96 18.08.96 25.34-.63a49.65 49.65 0 0 0 14.09-5.45v1.15a50.52 50.52 0 0 1-13.88 5.28c-7.38 1.61-13.08 1.61-25.63.65l-2.08-.16c-4.43-.33-7.39-.48-10.41-.48-20.3 0-38.2 3.72-58.81 11.96-7.01 2.8-11.7 3.8-16.97 3.88-1.5.02-2.39-.01-5.66-.14-9.76-.4-16.8-.01-29.47 2.3-15.06 2.73-24.32 9.58-30.71 20.31a72.8 72.8 0 0 0-3.9 7.63c-.12.28-2.39 5.47-3.01 6.79a22 22 0 0 1-3.1 5.1c-1.86 2.13-4.07 3.66-11.6 8.35-7.95 4.96-12.35 7.95-17.44 12.15-8.76 7.23-15.73 15.43-20.89 25.4-15.61 30.2-28.36 49.32-37.16 55.19-7.98 5.32-21.97 9.39-59.17 18.07-13.65 3.18-24.98 8.39-34.82 15.42-7.22 5.16-12.27 10.01-19.92 18.43a86.07 86.07 0 0 1-6.57 6.4c-1.67 1.48-7.91 6.83-7.84 6.77-3.27 2.84-5.8 5.16-8.26 7.62a92.1 92.1 0 0 0-14.27 18.13c-4.3 7.16-3.22 11.89 2.53 15.26 4.47 2.63 9.88 3.99 23.24 6.39a185.7 185.7 0 0 1 12.92 2.6c12.11 3.03 20.64.84 32.06-5.96l4.4-2.65c1.66-1 2.96-1.73 4.2-2.35.95-.48 6.04-3.14 7.6-3.92 9.59-4.8 17.04-6.94 24.49-5.98a24.1 24.1 0 0 1 10.4 3.93c7.82 5.21 8.45 12.52 4.7 25.13-.32 1.07-.64 2.1-1.16 3.74-2.57 8.12-3.31 11.04-3.31 14.55 0 10.88-5.66 23.14-15.08 37.28a41.28 41.28 0 0 0-3.97 7.46c-.37.9-.73 1.82-1.18 3.04-3.25 8.85-4.21 11.13-7.84 17.47-7.67 13.42-33.43 16.95-47.7 8.18a578.4 578.4 0 0 0-6.4-3.89c-3.4-2.04-16.36-9.67-17.67-10.45-8.38-4.97-16.36-9.78-25.92-15.66-9.5-5.85-15.7-7.7-19.24-6.36-2.68 1.02-3.8 3.82-4 8.51a61.12 61.12 0 0 0-.02 3.72l.02 1.7c0 4.5-2.69 8.73-11.52 19.87-3.92 4.95-5.87 7.59-7.55 10.39-2.39 3.97-4.08 10.56-6.56 23.72l-1.05 5.62-.86 4.4zm10.5 0h-1c.03-.34.04-.68.04-1 0-12.39 8.48-33.57 19.16-43.37a26.18 26.18 0 0 0 3.67-4.17 35.8 35.8 0 0 0 2.88-4.9c.36-.72 1.75-3.66 2.1-4.36 3.22-6.29 6.84-6.54 16.97.39 1.34.9 6.07 4.16 6.4 4.38 2.62 1.8 4.67 3.2 6.7 4.56 5.03 3.39 9.37 6.2 13.51 8.7 14.33 8.67 25.49 13.27 34.11 13.27 16.86 0 32.71-5.95 39.6-14.8 1.59-2.04 3.2-5.17 5.06-9.63.8-1.92 1.64-4.06 2.67-6.8l2.74-7.33c4.66-12.44 7.76-19.06 11.56-23.27 7.9-8.79 14.87-36 14.87-52.67 0-1.9.17-3.11 1.02-8.27.37-2.2.58-3.6.74-5.07.63-5.51.21-9.46-1.68-12.39-4.6-7.1-19.7-9.23-38.46-4.78a100.57 100.57 0 0 0-18.94 6.3c-5.17 2.37-17.11 9.74-16.5 9.4-6.72 3.64-12.97 4.15-24.8 1.3-29.55-7.14-30.43-8.62-15.26-26.81 17.44-20.93 47.12-46.18 56.38-46.18 9.92 0 53.84-11.98 65.78-17.95 9.46-4.73 24.32-21.18 36.82-37.85.71-.95 13.5-21.6 19.2-29.6 9.35-13.13 18.22-22.55 26.95-27.53 7.29-4.17 13.16-10.28 18.8-18.73 1.93-2.9 10.52-17.65 12.73-20.41 1.54-1.93 3-3.21 4.52-3.89 14.07-6.25 24.22-9.04 39.2-9.04h29c4.05 0 7.36-.4 22.93-2.5l4.3-.57c9.92-1.3 16.57-1.93 21.77-1.93 1.66 0 2.95.01 6.03.04 18.61.19 28.55-.48 44.86-4.03 3.1-.67 6.13-1.78 9.11-3.31v1.12a37.96 37.96 0 0 1-8.9 3.17c-16.4 3.56-26.4 4.24-45.08 4.05-3.08-.03-4.36-.04-6.02-.04-5.15 0-11.76.63-21.64 1.92l-4.3.58c-15.64 2.11-18.94 2.5-23.06 2.5h-29c-14.81 0-24.84 2.75-38.8 8.96-1.34.6-2.69 1.78-4.14 3.6-2.16 2.68-10.72 17.39-12.68 20.33-5.72 8.57-11.7 14.8-19.13 19.04-8.57 4.9-17.36 14.23-26.63 27.24-5.68 7.97-18.47 28.64-19.22 29.63-12.6 16.8-27.52 33.32-37.18 38.15-12.06 6.03-56.14 18.05-66.22 18.05-8.82 0-38.39 25.15-55.62 45.82-14.6 17.52-14.19 18.21 14.74 25.2 11.6 2.8 17.6 2.3 24.09-1.2-.67.35 11.31-7.03 16.56-9.44 5.41-2.48 11.6-4.59 19.11-6.37 19.13-4.53 34.65-2.35 39.54 5.22 2.05 3.17 2.48 7.32 1.84 13.04a96.34 96.34 0 0 1-.75 5.13c-.84 5.08-1.01 6.29-1.01 8.1 0 16.9-7.03 44.33-15.13 53.33-3.68 4.09-6.76 10.65-11.37 22.96-.35.93-2.2 5.94-2.73 7.33-1.04 2.76-1.88 4.9-2.68 6.84-1.9 4.53-3.55 7.73-5.2 9.85-7.1 9.13-23.25 15.19-40.39 15.19-8.86 0-20.15-4.65-34.63-13.42-4.15-2.51-8.5-5.32-13.55-8.72a861.54 861.54 0 0 1-6.71-4.56l-6.4-4.39c-9.68-6.63-12.61-6.42-15.5-.75-.35.68-1.74 3.62-2.1 4.35a36.77 36.77 0 0 1-2.96 5.03c-1.12 1.57-2.37 3-3.81 4.33-10.47 9.6-18.84 30.51-18.84 42.63l-.03 1zm-29.65 0h-1.1c1.17-2.52 1.79-5.2 1.79-8 0-20 4.83-42.04 12.15-49.35 5.17-5.18 7.77-8.38 9.9-12.74 2.64-5.41 3.95-12 3.95-20.91 0-6.82 1.14-11.59 3.37-15.07 1.74-2.7 3.6-4.21 8.91-7.52a31.64 31.64 0 0 0 3.9-2.79c4.61-3.96 6.58-6.2 7.72-9.41 1.43-4.02.93-9.04-1.86-16.02a68.98 68.98 0 0 0-3.99-8.07l-.93-1.7a75.47 75.47 0 0 1-2.64-5c-5.16-10.71-3.77-18.9 7.68-29.78a204 204 0 0 1 26.81-21.55c3.96-2.69 16.8-10.8 19.24-12.5 1.99-1.4 4.33-3.3 7.77-6.3-.02 0 7.23-6.39 9.47-8.3 4.97-4.26 9.09-7.5 13.05-10.15 4.72-3.15 8.97-5.28 12.87-6.32 12.78-3.41 15.6-4.18 21.77-5.97 12.55-3.64 21.96-6.9 28.14-10a45.47 45.47 0 0 1 7.47-2.79c8.66-2.66 12.02-4.1 16.97-8.1 6.78-5.46 13.07-14.25 19.33-27.87 15.97-34.77 19.08-39.39 32.15-49.19 3.14-2.36 6.37-4.1 11.43-6.4l2.33-1.04c11.93-5.35 16.87-8.93 21.1-17.38 1.88-3.77 2.48-6.29 3.37-12.27.78-5.19 1.48-7.56 3.53-10.25 2.57-3.4 7.03-6.27 14.36-9.01 3.37-1.26 7.36-2.5 12.05-3.73 16.33-4.3 25.28-5.36 39.6-5.81 6.9-.22 9.5-.56 12.66-2 1.19-.54 2.36-1.23 3.58-2.11 3.7-2.7 8.14-4.54 13.24-5.67 5.71-1.27 10.69-1.54 18.7-1.45l2.35.02c2.82 0 6.8-1 19.7-4.69 10.83-3.08 15.95-4.31 19.3-4.31.82 0 1.9.13 3.55.41l5.01.9c9.82 1.68 17.44 1.89 25.15-.21 7.98-2.18 14.8-6.77 20.29-14.24V147c-5.47 7.04-12.21 11.42-20.03 13.55-7.88 2.15-15.63 1.94-25.58.23l-5-.9c-1.6-.26-2.64-.39-3.39-.39-3.2 0-8.32 1.22-19.74 4.48-12.35 3.53-16.3 4.52-19.26 4.52l-2.36-.02c-7.94-.1-12.85.17-18.47 1.42-4.97 1.11-9.3 2.9-12.88 5.5a21.4 21.4 0 0 1-3.75 2.22c-3.32 1.5-6 1.87-13.04 2.09-14.25.44-23.13 1.5-39.37 5.77a125.56 125.56 0 0 0-11.95 3.7c-7.17 2.7-11.49 5.46-13.93 8.68-1.9 2.52-2.58 4.76-3.33 9.8-.9 6.08-1.53 8.68-3.47 12.56a30.6 30.6 0 0 1-9.66 11.45c-3.12 2.26-5.95 3.73-11.93 6.4l-2.31 1.04c-5.01 2.27-8.18 3.99-11.25 6.29-12.9 9.68-15.93 14.17-31.85 48.8-6.31 13.76-12.7 22.68-19.6 28.25-5.08 4.1-8.53 5.57-17.3 8.27a44.64 44.64 0 0 0-7.33 2.73c-6.24 3.12-15.7 6.4-28.3 10.06a867.4 867.4 0 0 1-21.8 5.97c-3.77 1.01-7.93 3.1-12.56 6.19a137.35 137.35 0 0 0-12.95 10.07c-2.24 1.92-9.48 8.3-9.48 8.3a98.2 98.2 0 0 1-7.84 6.37c-2.46 1.72-15.32 9.83-19.26 12.5a203 203 0 0 0-26.69 21.45c-11.13 10.58-12.43 18.3-7.47 28.63a74.52 74.52 0 0 0 2.62 4.95l.94 1.7a69.84 69.84 0 0 1 4.03 8.17c2.88 7.2 3.4 12.46 1.89 16.73-1.22 3.43-3.28 5.77-8.02 9.84-1.14.97-2.32 1.8-5.3 3.67-3.92 2.45-5.69 3.89-7.31 6.42-2.13 3.3-3.22 7.89-3.22 14.53 0 9.05-1.34 15.79-4.05 21.34-2.19 4.49-4.85 7.77-10.1 13.01-7.07 7.07-11.85 28.9-11.85 48.65 0 2.8-.58 5.48-1.7 8zm282.54 0h-1.01l-1.1-5.8c-3.08-16.26-4.05-26.2-2.74-37.26.7-5.8.77-9.68.55-15.3-.18-4.45-.17-5.68.19-7.63.78-4.3 3.44-8.53 10.39-16.34 9.07-10.2 12.26-15.41 19.8-30.15 1.35-2.64 2.33-4.47 3.38-6.3.9-1.58 1.82-3.06 2.77-4.5 3.14-4.7 7.03-8.42 16.84-16.81 11.22-9.6 15.5-13.86 18.13-19.13.7-1.4 1.3-2.8 1.93-4.4a206 206 0 0 0 1.49-4.05c3.63-9.94 8.01-13.93 22.9-17.81 4.99-1.3 20.55-5.13 21.38-5.34 16.19-4.1 25.33-7.36 33.48-12.6 5.86-3.77 5.84-3.76 27.66-16.53l2.6-1.52c10.23-6 17.1-10.2 22.73-13.95a149.3 149.3 0 0 0 8.8-6.3 723.7 723.7 0 0 0 6.37-5.08A87.74 87.74 0 0 1 600 342.95v1.12a85.76 85.76 0 0 0-15.49 9.9c.18-.14-4.76 3.84-6.38 5.1a150.3 150.3 0 0 1-8.85 6.35c-5.65 3.76-12.53 7.96-22.78 13.97l-2.6 1.53c-21.8 12.75-21.78 12.74-27.63 16.5-8.27 5.32-17.49 8.61-33.78 12.73-.83.21-16.39 4.04-21.36 5.33-8.03 2.1-13.15 4.5-16.45 7.5-2.66 2.42-4 4.86-5.77 9.7l-1.5 4.07a51.12 51.12 0 0 1-1.96 4.47c-2.72 5.45-7.04 9.75-18.38 19.45-9.73 8.32-13.6 12.02-16.65 16.6a77.18 77.18 0 0 0-2.74 4.45c-1.05 1.81-2.01 3.63-3.35 6.25-7.58 14.81-10.82 20.08-19.96 30.36-6.83 7.7-9.4 11.78-10.15 15.86-.34 1.85-.34 3.04-.17 7.4.22 5.68.14 9.6-.55 15.47-1.3 10.92-.34 20.79 2.73 36.95l1.12 5.99zm-76.59 0h-2.1l1.39-4.3c1.04-3.3 1.93-6.78 2.68-10.4 2.65-12.73 3.27-23.63 3.27-41.3 0-5.71-1.86-9.75-4.13-9.75-2.94 0-6.96 5.61-10.93 17.08C271.14 579.68 258.3 593 238 593c-22.42 0-29.26-1.35-48.42-10.09a87.69 87.69 0 0 1-9.42-5.04c-2.95-1.8-12.78-8.57-14.84-9.72-4.2-2.36-7-2.71-9.72-.99-.63.4-1.26.91-1.9 1.55a57.69 57.69 0 0 1-4.31 3.86 147.88 147.88 0 0 1-3.06 2.44l-1 .8C137.01 582.43 134 587.18 134 597c0 1.02-.02 2.01-.07 3h-2c.05-.99.07-1.98.07-3 0-10.52 3.33-15.78 12.09-22.76a265.61 265.61 0 0 1 2-1.6c.83-.64 1.43-1.13 2.03-1.61a55.76 55.76 0 0 0 4.17-3.74c.74-.73 1.48-1.34 2.24-1.82 3.47-2.2 7-1.75 11.77.93 2.15 1.21 12.03 8 14.9 9.76a85.7 85.7 0 0 0 9.22 4.93C209.29 589.7 215.85 591 238 591c19.25 0 31.49-12.7 41.06-40.33 4.24-12.25 8.66-18.42 12.81-18.42 3.8 0 6.13 5.06 6.13 11.75 0 17.8-.63 28.8-3.3 41.7-.77 3.7-1.68 7.23-2.75 10.6-.4 1.3-.8 2.53-1.19 3.7zm-149.25 0l.5-.94a160.1 160.1 0 0 0 6.53-13.26c2.73-6.29 5.78-9.64 9.24-10.52 3.74-.95 7.15.74 12.56 5.13 5.43 4.4 6.07 4.86 7.73 5.1 1.6.22 4.28 1.14 8.86 2.95 1.3.5 10.78 4.35 13.85 5.55 3.07 1.2 5.85 2.25 8.49 3.18 3.1 1.1 5.98 2.04 8.65 2.81h-3.45c-1.76-.56-3.6-1.18-5.54-1.87a281.2 281.2 0 0 1-8.51-3.19c-3.08-1.2-12.57-5.04-13.86-5.55-4.5-1.78-7.15-2.68-8.63-2.9-1.94-.27-2.53-.7-8.22-5.3-5.17-4.2-8.36-5.78-11.69-4.94-3.1.78-5.94 3.92-8.56 9.95a161 161 0 0 1-6.82 13.8h-1.13zm112.89 0a30.34 30.34 0 0 0 11.27-6.27c1.55-1.36 3.32-3.46 5.34-6.29 1.05-1.46 2.15-3.1 3.41-5.04a349.73 349.73 0 0 0 2.5-3.9l.47-.75.93-1.47a89.17 89.17 0 0 1 3.25-4.86c1.05-1.43 1.82-2.23 2.44-2.46 1.02-.37 1.49.48 1.49 2.04l.01 2.11c.05 6.91-.08 11.32-.7 16.33a48.4 48.4 0 0 1-2.38 10.56h-1.07a46.47 46.47 0 0 0 2.45-10.68c.62-4.96.75-9.33.7-16.2l-.01-2.12c0-.97-.08-1.12-.15-1.1-.36.14-1.05.85-1.97 2.1a88.44 88.44 0 0 0-3.22 4.82l-.92 1.46-.48.75a1268.1 1268.1 0 0 1-2.5 3.92c-1.26 1.95-2.38 3.6-3.44 5.08-2.06 2.88-3.87 5.04-5.5 6.45a30.87 30.87 0 0 1-8.94 5.52h-2.98zm-183.72 0H69.3c3.37-3.43 5.19-8.33 5.19-15 0-18.6-.04-17.35 1.02-20.77.6-1.93 1.5-3.74 3.27-6.63.42-.7 4.92-7.8 6.78-10.86 3.04-4.97 11.04-16.5 12.21-18.56 3.48-6.08 4.72-12.06 4.72-24.18 0-7.85 2.5-14.2 8.1-23.44l2.84-4.63a72.67 72.67 0 0 0 2.49-4.4c1.62-3.15 2.48-5.78 2.62-8.28.2-3.78-1.3-7.29-4.9-10.9-5.13-5.12-8.6-5.43-11.2-1.85-2.12 2.92-3.48 7.74-5.06 16.47-.2 1.03-.82 4.6-.82 4.57-.83 4.67-1.4 7.33-2.1 9.6-1.35 4.42-3.7 7.61-8.36 12.26l-3.26 3.2c-6.38 6.39-9.68 11.51-11.36 19.5l-1.16 5.52c-.87 4.1-1.56 7.04-2.33 9.94-3.67 13.74-9.65 25.97-22.59 44.72-7.68 11.14-11.05 18.87-10.92 23.72h-1c-.12-5.16 3.35-13.05 11.1-24.28 12.87-18.67 18.8-30.8 22.44-44.42.77-2.88 1.45-5.8 2.32-9.89l1.16-5.51c1.73-8.22 5.13-13.5 11.64-20 .63-.64 2.84-2.8 3.25-3.21 4.57-4.54 6.82-7.62 8.12-11.84a81.58 81.58 0 0 0 2.07-9.48l.81-4.57c1.62-8.9 3-13.8 5.24-16.89 3-4.15 7.2-3.78 12.71 1.74 3.8 3.8 5.42 7.58 5.2 11.66-.15 2.66-1.05 5.41-2.73 8.68a73.6 73.6 0 0 1-2.52 4.46l-2.84 4.63c-5.52 9.1-7.96 15.3-7.96 22.92 0 12.28-1.28 18.43-4.85 24.68-1.2 2.1-9.21 13.65-12.22 18.58-1.87 3.06-6.37 10.18-6.78 10.86-1.73 2.82-2.6 4.57-3.17 6.4-1.02 3.28-.98 2.1-.98 20.48 0 6.52-1.7 11.44-4.82 15zM310.09 0h1.06c-.37.9-.77 1.83-1.2 2.82-3.9 9.06-5.45 15.15-5.45 25.18 0 7.64-2.1 11.6-6.64 13.05-3.46 1.1-5.72.98-17.57-.43-11.55-1.36-19.17-1.58-28.16-.14-6.24 2.49-25.91 7.02-32.13 7.02-11.15 0-36.76-2.88-54.12-7.01a22.08 22.08 0 0 0-16.95 2.48c-4.05 2.33-7.09 5.03-13.9 11.97-6.28 6.39-9.53 9.23-13.8 11.5-7.09 3.79-11.22 7.65-13.4 12.27-1.82 3.85-2.33 7.84-2.33 15.29 0 4.4-2.65 6.69-9.45 9.74.1-.05-2.97 1.31-3.84 1.71-8.78 4.06-12.71 8.29-12.71 16.55 0 12.52-4.86 19.22-17.34 27.96l-4.56 3.14c-1.9 1.3-3.3 2.3-4.67 3.3-.92.68-1.79 1.34-2.62 2-7.16 5.62-11 14.54-15.56 33.28-.63 2.57-3.3 14-4.07 17.14a350.44 350.44 0 0 1-5.2 19.33c-1.37 4.5-4.5 15.07-4.96 16.53-1.05 3.4-1.64 4.94-2.46 6.32-.82 1.4-6.85 9.08-12.64 18.27L0 277.98v-1.9l4.58-7.35a270.8 270.8 0 0 1 12.61-18.23c-.3.5 1.35-2.8 2.38-6.12.45-1.44 3.58-12.01 4.95-16.53 1.83-6.03 3.44-12.09 5.19-19.27.76-3.13 3.44-14.56 4.06-17.14 4.62-18.95 8.52-28.02 15.92-33.83.84-.67 1.72-1.33 2.65-2.01 1.38-1.02 2.8-2.01 4.7-3.32l4.54-3.14C73.83 140.57 78.5 134.13 78.5 122c0-8.74 4.2-13.26 13.29-17.45.88-.41 3.96-1.77 3.85-1.73 6.46-2.9 8.86-4.97 8.86-8.82 0-7.6.53-11.7 2.42-15.71 2.29-4.84 6.57-8.85 13.84-12.73 4.15-2.21 7.35-5 14.15-11.93 6.28-6.4 9.36-9.13 13.52-11.53a23.07 23.07 0 0 1 17.69-2.59c17.27 4.12 42.8 6.99 53.88 6.99 6.1 0 25.73-4.53 31.92-7 9.12-1.46 16.83-1.25 28.49.13 11.63 1.38 13.9 1.5 17.15.47 4.06-1.3 5.94-4.85 5.94-12.1 0-10.1 1.56-16.3 6.6-28zm25.12 0h1c.05 5.62.26 11.48.65 19.4.47 9.7.64 14.57.64 21.6 0 9.81-4.68 17.46-13.1 23.16-6.53 4.43-14.94 7.46-24.33 9.33-3.74.54-9.42.56-22.68.23-6.74-.17-9.35-.22-12.39-.22-2.77 0-4.97.43-7.63 1.36-.88.3-4.55 1.74-5.58 2.11-6.55 2.35-13.59 3.53-24.79 3.53-8.1 0-13.58-1.38-22.46-4.9l-3.18-1.25c-12.55-4.87-21.27-5.15-37.18 1.12-11.15 4.39-18.13 9.2-22.28 14.81-3.15 4.26-4.33 7.8-5.94 15.8-1.22 6.09-1.93 8.74-3.5 12.13-1.65 3.53-3.97 5.81-7.07 7.22-2.33 1.07-4.35 1.5-9.32 2.19-9.04 1.27-12.77 3.09-15.61 9.58-3.71 8.48-7.72 13.87-14.22 19.76-2.4 2.18-13.14 11.02-15.91 13.42-8.2 7.1-13.85 17.37-18.7 31.97a258.81 258.81 0 0 0-3.27 10.7c-.01.05-2.26 7.97-2.88 10.1-8.49 28.85-17.88 52.95-26.13 61.2-2.8 2.8-5.06 5.64-10.4 12.96-3.4 4.68-6.23 8.25-8.95 11.1v-1.55c2.74-2.98 5.73-6.82 9.48-11.97 4.03-5.52 6.32-8.4 9.17-11.24 8.07-8.08 17.44-32.14 25.87-60.8.62-2.1 2.86-10.03 2.88-10.08 1.21-4.24 2.21-7.53 3.28-10.74 4.9-14.75 10.63-25.16 19-32.4 2.78-2.42 13.5-11.25 15.89-13.4 6.4-5.8 10.32-11.09 13.97-19.43 1.68-3.83 4.05-6.31 7.2-7.86 2.4-1.17 4.64-1.67 9.53-2.36 4.54-.63 6.5-1.05 8.7-2.06 2.89-1.31 5.03-3.42 6.58-6.73 1.53-3.3 2.23-5.9 3.43-11.9 1.64-8.14 2.85-11.79 6.11-16.2 4.28-5.79 11.41-10.7 22.73-15.16 16.15-6.36 25.13-6.07 37.9-1.11l3.19 1.26c8.77 3.47 14.13 4.82 22.09 4.82 11.09 0 18.02-1.16 24.46-3.47 1-.36 4.68-1.8 5.58-2.11A22.5 22.5 0 0 1 265 72.5c3.05 0 5.67.05 14.07.26 11.53.29 17.2.27 20.83-.25 9.25-1.85 17.54-4.83 23.94-9.17C332 57.8 336.5 50.46 336.5 41c0-7-.17-11.86-.7-22.7-.35-7.26-.55-12.83-.59-18.3zM93.87 0h2.04c-.7 4-1.61 6.82-3.03 9.47-2.33 4.38-2.85 5.75-5.26 13.03a40.46 40.46 0 0 1-1.94 5.03c-2.24 4.66-5.92 8.8-13.07 14.26-8.01 6.13-14.27 16.55-20.03 31.55-2.4 6.23-8.75 25.63-9.64 28.01-2.69 7.16-6.56 12.7-15.63 23.68l-2.68 3.24c-6.02 7.34-9.35 12.07-11.72 17.15-2.3 4.94-7.12 9.9-12.91 14.15v-2.4c5.14-3.94 9.1-8.3 11.1-12.6 2.46-5.27 5.87-10.1 11.98-17.56l2.68-3.26c8.94-10.8 12.72-16.22 15.3-23.1.88-2.33 7.24-21.74 9.65-28.03 5.89-15.31 12.3-26 20.68-32.41 6.92-5.3 10.4-9.2 12.48-13.55.65-1.35 1.16-2.7 1.85-4.79 2.45-7.4 3-8.83 5.4-13.34A27.68 27.68 0 0 0 93.87 0zm9.07 0h1.02c-1.66 8.3-2.91 12.67-4.54 15.26a59.14 59.14 0 0 0-4.1 8.21c-1.27 3-2.44 6.2-3.5 9.4-.38 1.12-.7 2.16-2.41 5.39a251.48 251.48 0 0 0-12.81 13.3c-3.48 3.96-5.95 7.27-7.15 9.66-.95 1.9-2.06 5.99-3.61 12.97-.64 2.9-3.65 17.15-4.51 21.07-3.63 16.45-6.63 26.69-9.9 32-7.66 12.45-10.64 15.71-37.08 41.1A69.78 69.78 0 0 1 0 179.21v-1.15a69.39 69.39 0 0 0 13.65-10.42c26.4-25.33 29.32-28.55 36.92-40.9 3.2-5.18 6.18-15.37 9.78-31.7.86-3.91 3.87-18.16 4.51-21.06 1.57-7.09 2.7-11.2 3.7-13.2 1.24-2.5 3.76-5.86 7.29-9.89.9-1.03 1.86-2.1 2.86-3.18 2.4-2.6 4.96-5.22 7.53-7.76.9-.88 1.73-1.7 3.37-3.4a129.02 129.02 0 0 1 4.78-13.46 60.07 60.07 0 0 1 4.19-8.35c1.52-2.44 2.74-6.71 4.36-14.74zM83.71 0h1.1c-2.09 4.74-6.03 8.92-11.42 12.3-7.2 4.52-16.5 7.2-24.39 7.2-8.9 0-11.8 7-11.74 21.52 0 1.7.04 3.17.12 5.99.1 3.3.12 4.45.12 5.99 0 5.73-.76 11.3-2.01 16.5a66.67 66.67 0 0 1-2.15 6.97 2597.76 2597.76 0 0 1-7 15.86A4270.8 4270.8 0 0 1 6.44 136.2 54.64 54.64 0 0 1 0 147v-1.65a54.87 54.87 0 0 0 5.55-9.57A4269.82 4269.82 0 0 0 30.7 79.97c.53-1.2.99-2.23 2.44-5.9A69.23 69.23 0 0 0 36.5 53c0-1.52-.03-2.66-.12-5.95-.08-2.83-.12-4.31-.12-6.01-.03-6.79.53-11.62 2.07-15.34 1.94-4.68 5.39-7.19 10.67-7.19 7.7 0 16.81-2.63 23.86-7.05C77.93 8.27 81.66 4.38 83.7 0zm282.63 0h1.01c1.86 10.02 2.18 12.67 2.32 18.3a123.43 123.43 0 0 1 .37 27.83c-.96 8.78-3.1 16.01-6.63 21.15-11.34 16.5-39.8 29.22-66.41 29.22-5.09 0-10.47.28-16.31.83a413.8 413.8 0 0 0-24.37 3.16c-21.56 3.26-27.66 4.01-36.32 4.01-6.92 0-12.2-1.05-21.69-3.9l-2.78-.83c-1.39-.41-2.54-.74-3.65-1.02-8-2.05-14.22-2.04-21.7.72a16.32 16.32 0 0 0-9.17 8.18c-1.6 3.05-2.5 6.06-4.02 12.83-1.5 6.64-2.34 9.52-3.99 12.64a16.16 16.16 0 0 1-9.85 8.36 104.8 104.8 0 0 0-9.5 3.42c-6.55 2.8-10.1 5.57-13.8 10.47-1.33 1.75-1.03 1.3-5.43 7.9-1.98 2.97-4.66 5.8-8.48 9.14-2.01 1.76-10.71 8.83-12.88 10.7-7.37 6.35-12.58 12.14-16.63 19.14-4.22 7.3-7.8 18.3-11.28 33.26-.87 3.73-1.72 7.64-2.64 12.14l-1.18 5.8-1.09 5.45c-1.8 8.96-2.77 13.28-3.77 16.26-6.8 20.44-17.26 42.16-27.13 51.2-5.11 4.7-8.1 7.07-11.1 8.86-.9.54-1.84 1.04-2.92 1.57-.44.22-9.6 4.4-14.1 6.66l-1.22.62v-1.13l.78-.39c4.52-2.26 13.67-6.44 14.1-6.65a41.19 41.19 0 0 0 2.84-1.54c2.94-1.75 5.88-4.09 10.94-8.73 9.71-8.9 20.1-30.51 26.87-50.79.97-2.92 1.94-7.22 3.73-16.13l1.1-5.46a490.5 490.5 0 0 1 3.82-17.96c3.5-15.06 7.1-26.14 11.39-33.54 4.11-7.11 9.4-12.98 16.83-19.4 2.19-1.88 10.88-8.95 12.88-10.7 3.77-3.28 6.39-6.05 8.3-8.93 4.43-6.64 4.12-6.18 5.47-7.96 3.8-5.03 7.5-7.91 14.21-10.78 2.61-1.12 5.74-2.24 9.59-3.46a15.17 15.17 0 0 0 9.27-7.86c1.59-3.02 2.42-5.85 4.03-12.99 1.41-6.27 2.32-9.33 3.98-12.48a17.31 17.31 0 0 1 9.7-8.66c7.7-2.83 14.1-2.84 22.3-.75 1.12.29 2.28.61 3.68 1.03l3.73 1.11c8.47 2.54 13.66 3.58 20.46 3.58 8.59 0 14.67-.75 36.18-4a414.64 414.64 0 0 1 24.41-3.17c5.88-.54 11.29-.83 16.41-.83 26.3 0 54.45-12.58 65.59-28.78 3.42-4.98 5.5-12.06 6.46-20.7.84-7.74.73-16.02.02-23.9a136.2 136.2 0 0 0-.57-5.12c0-4.47-.3-6.94-2.16-17zM18.88 0h1.03C18 7.57 17.15 10.18 14.46 16.2c-1.95 4.37-2.67 9.19-2.42 14.89.2 4.33.71 7.7 2.28 16.13 1.09 5.88 1.57 8.77 1.94 12.2.96 8.9.24 16.08-2.8 22.79A463.4 463.4 0 0 1 0 109.43v-2.12a465 465 0 0 0 12.54-25.52c2.97-6.52 3.67-13.53 2.72-22.27-.36-3.4-.84-6.26-1.93-12.12-1.57-8.47-2.1-11.88-2.29-16.27-.26-5.84.48-10.81 2.5-15.33 2.64-5.9 3.48-8.47 5.34-15.8zm280.47 0a70.78 70.78 0 0 1-4.91 11.24c-2.56 4.7-4.01 8.45-4.86 11.98l-.4 1.8-.28 1.45a5.28 5.28 0 0 1-.74 2.07c-.74 1.03-1.93 1.28-5.13 1.25.92 0-9.85-.29-15.03-.29-10.2 0-18.45.82-29.46 2.56-16.87 2.66-17.73 2.77-23.66 2.52a42.57 42.57 0 0 1-8-1.09c-17.7-4.16-46.18-5.86-54.72-3.01-2.72.9-5.88 2.8-9.52 5.59a112.37 112.37 0 0 0-6.54 5.48c-1.4 1.25-9.17 8.5-10.78 9.84-1.45 1.2-8.18 7.42-8.85 8.02a114.65 114.65 0 0 1-4.55 3.9c-4.99 4.03-8.9 6.2-11.92 6.2-3.52.05-4.32 0-5.14-.4-1.13-.56-1.5-1.72-1.13-3.57.74-3.63 4.47-10.84 12.84-24.8 5.69-9.48 9.42-18 11.78-26.2 1.45-5.04 1.94-7.4 2.97-14.54h1.01c-1.05 7.3-1.54 9.7-3.01 14.82-2.39 8.28-6.16 16.89-11.9 26.44-8.3 13.84-12 21.01-12.7 24.48-.3 1.45-.08 2.14.59 2.47.6.3 1.35.35 3.48.3 3.92 0 7.69-2.1 12.5-5.98 1.4-1.13 2.87-2.39 4.51-3.86.66-.59 7.41-6.83 8.88-8.05 1.59-1.33 9.34-8.55 10.75-9.82 2.4-2.15 4.55-3.96 6.6-5.53 3.72-2.85 6.97-4.8 9.81-5.74 8.76-2.92 37.41-1.22 55.27 2.99 2.57.6 5.14.95 7.81 1.06 5.84.25 6.7.14 23.47-2.51 11.05-1.75 19.36-2.57 29.6-2.57 5.2 0 15.99.3 15.05.29 2.87.03 3.84-.17 4.3-.83.23-.32.4-.8.58-1.7l.28-1.43.4-1.85c.88-3.6 2.36-7.44 4.96-12.22 1.87-3.43 3.44-7 4.73-10.76h1.06zm-8.59 0c-5.91 17.94-9.55 22-19.76 22-4.5 0-10.22.32-28.69 1.5l-1.53.1c-15.6.99-23.47 1.4-28.78 1.4-5.35 0-13.24-.96-28.86-3.28l-1.54-.23C163.18 18.75 157.47 18 153 18c-4.45 0-7.3 1.01-10.96 3.34-.1.06-1.8 1.17-2.3 1.47-2.43 1.5-4.32 2.19-6.74 2.19-2.8 0-4.11-1.46-4.11-4.22 0-1.04.16-2.29.5-4.1.16-.82.9-4.4 1.07-5.32.8-4.11 1.3-7.68 1.47-11.36h2c-.17 3.82-.68 7.5-1.5 11.75-.19.94-.92 4.5-1.07 5.31a21.04 21.04 0 0 0-.47 3.72c0 1.7.46 2.22 2.11 2.22 1.99 0 3.55-.57 5.7-1.9.47-.28 2.15-1.37 2.26-1.44C144.92 17.14 148.12 16 153 16c4.62 0 10.3.74 28.9 3.51l1.53.23C198.93 22.04 206.8 23 212 23c5.25 0 13.11-.41 28.65-1.4l1.54-.1C260.73 20.32 266.43 20 271 20c8.95 0 12.15-3.4 17.66-20h2.1zM141.51 0h1.13c-2.06 3.86-2.63 5.1-2.77 6.19-.15 1.12.42 1.64 2.32 1.96 1.8.3 3.85.35 10.81.35 6.02 0 13 .56 21.35 1.62 3.95.5 8.03 1.1 13.13 1.89 24 3.7 22.5 3.49 26.83 3.49 24.02 0 51.83-2.24 60.45-6.94 2.88-1.57 5.05-4.49 6.6-8.56h1.07c-1.64 4.47-3.98 7.69-7.2 9.44-8.83 4.82-36.67 7.06-60.92 7.06-4.41 0-2.84.22-26.98-3.5-5.1-.8-9.17-1.38-13.1-1.88-8.31-1.06-15.26-1.62-21.23-1.62-7.04 0-9.1-.05-10.97-.37-2.38-.4-3.38-1.32-3.15-3.07.16-1.22.69-2.41 2.63-6.06zm76.4 0c5.69 1.64 10.37 2.5 14.09 2.5 9.59 0 16.7-.71 22.4-2.5h2.98C251.12 2.53 243.2 3.5 232 3.5c-4.5 0-10.32-1.21-17.53-3.5h3.45zM70.69 0c-2.87 3.27-6.95 5.39-12.02 6.53-3.98.89-7.5 1.08-12.92 1A97.24 97.24 0 0 0 44 7.5c-5.37 0-8.86-1.24-10.1-4.97A8.6 8.6 0 0 1 33.5 0h.99c.02.82.14 1.56.36 2.22C35.91 5.39 39.02 6.5 44 6.5l1.76.02c5.35.09 8.8-.1 12.69-.97C62.95 4.54 66.63 2.74 69.3 0h1.37zM0 207.87c7.31-.16 11.5 3.33 11.5 11.13 0 11.41-5.05 28.35-11.5 41.5v-2.3c5.93-12.72 10.5-28.47 10.5-39.2 0-7.18-3.7-10.3-10.5-10.13v-1zm0 7.05c1.23.14 2.18.58 2.87 1.31 1.4 1.48 1.6 3.72 1.16 7.58l-.16 1.3A28.93 28.93 0 0 0 3.5 229c0 3.2-1.48 9.52-3.5 15.9v-3.45c1.49-5.13 2.5-9.87 2.5-12.45 0-.98.08-1.75.37-4.02l.16-1.29c.42-3.56.24-5.59-.88-6.77-.5-.53-1.21-.87-2.15-1v-1zM0 410.9v-1.47a21.67 21.67 0 0 0 2.97-4.7c1.32-2.7 2.68-6.28 4.56-11.89 7.85-23.55 7.83-26.6.25-30.4-2.25-1.12-4.8-1.43-7.78-.91v-1.02a13.1 13.1 0 0 1 8.22 1.04c8.24 4.12 8.26 7.6.25 31.6-1.88 5.66-3.25 9.27-4.6 12.02A20.82 20.82 0 0 1 0 410.9zM33.64 452c1.68 0 3.04-.23 8.34-1.31l2.38-.47c8.26-1.57 12.72-1.3 14.53 2.33 1.38 2.75-.47 5.86-4.75 9.68a75.6 75.6 0 0 1-5.08 4.07c-.94.7-4.89 3.59-5.79 4.27-1.86 1.4-2.97 2.37-3.47 3.03a19.08 19.08 0 0 0-2.89 5.5c.07-.2-4.02 13.65-6.96 22.22-2.7 7.85-5.56 10.72-8.82 8.59-2.11-1.4-3.66-4.24-6.6-11.03-1.98-4.62-2.5-5.76-3.4-7.4-4.55-8.18-3.9-23.9-.05-32.87a9.6 9.6 0 0 1 6.98-5.96c2.59-.66 4.86-.75 11.78-.67l3.8.02zm0 2c-1.13 0-2.09 0-3.82-.02-12.07-.13-14.83.57-16.9 5.41-3.63 8.47-4.26 23.55-.05 31.12.96 1.73 1.48 2.88 3.5 7.58 2.72 6.3 4.24 9.08 5.86 10.14 1.64 1.08 3.5-.8 5.82-7.55a682.9 682.9 0 0 0 6.97-22.24 21.03 21.03 0 0 1 3.18-6.04c.65-.87 1.85-1.9 3.86-3.43.92-.7 4.87-3.57 5.8-4.27 2.02-1.5 3.6-2.77 4.95-3.97 3.63-3.23 5.09-5.7 4.3-7.28-1.21-2.42-5.07-2.65-12.38-1.27l-2.35.47c-5.49 1.11-6.86 1.35-8.74 1.35zm345.63 146c-3.45-12.26-3.77-14.13-3.77-19 0-3.33-.13-6.27-.43-11.34-.63-10.33-.65-13.5.26-17.07 1.21-4.74 4.21-7.1 9.67-7.1h26c4.08 0 5.19 1.85 5.93 7.11.1.79.13.97.19 1.32.84 5.35 2.8 7.58 8.88 7.58 3.64 0 5.54.4 6.43 1.37.76.83.76 1.44.36 3.93-.85 5.26.5 8.85 7.5 13.8 6.32 4.45 11.63 5.36 16.55 3.37 3.8-1.54 6.73-4.16 11.92-10l1.1-1.23 1.09-1.23a75.6 75.6 0 0 1 2.7-2.86 35.81 35.81 0 0 1 9.57-6.73c1.52-.76 1.72-.86 5.66-2.63 6.1-2.73 9.01-4.5 11.74-7.62 2.63-3 4.67-4.85 6.7-6.04 3.18-1.85 5.46-2.13 13.68-2.13 5.98 0 10.56-4.32 18-14.99l2.82-4.03c1.06-1.5 1.94-2.7 2.79-3.79 7.87-10.12 19.38-10.4 30.74.96 5.54 5.53 10.17 19.43 13.64 38.51 2.5 13.75 4.18 29.46 4.47 39.84h-1c-.3-10.32-1.96-25.97-4.45-39.66-3.43-18.87-8.02-32.65-13.36-37.99-10.95-10.95-21.76-10.68-29.26-1.04-.83 1.07-1.7 2.26-2.75 3.75l-2.81 4.02c-7.65 10.95-12.38 15.42-18.83 15.42-8.04 0-10.21.26-13.17 2-1.92 1.12-3.9 2.9-6.45 5.83-2.86 3.26-5.87 5.09-12.09 7.88a103.35 103.35 0 0 0-5.62 2.6 34.84 34.84 0 0 0-9.32 6.54 74.67 74.67 0 0 0-3.75 4.05l-1.1 1.24c-5.28 5.95-8.29 8.64-12.28 10.25-5.26 2.13-10.92 1.17-17.5-3.48-7.33-5.17-8.82-9.15-7.92-14.77.34-2.12.34-2.6-.1-3.1-.64-.69-2.34-1.04-5.7-1.04-6.63 0-8.96-2.63-9.87-8.42l-.2-1.34c-.67-4.82-1.53-6.24-4.93-6.24h-26c-5 0-7.6 2.04-8.7 6.34-.88 3.43-.85 6.57-.23 16.76a177 177 0 0 1 .43 11.4c0 4.78.32 6.63 3.81 19h-1.04zm13.68 0c-1.31-6.58-1.61-10.71-1.36-14.84.04-.7.1-1.44.18-2.38l.23-2.56c.34-3.81.5-6.97.5-11.22 0-4.94 1.46-7.76 4.21-8.42 2.38-.58 5.56.54 9.2 3 6.64 4.52 13.99 13.07 16.55 19.23 4.77 11.44 14.12 15.69 33.54 15.69 8.6 0 14.32-2.35 20.67-7.88 1.45-1.26 15.06-15 21-20 7.21-6.07 11.77-7.59 20.62-8.32 5.52-.45 7.98-.9 11.44-2.36 4.58-1.95 9.36-5.48 14.9-11.29 7.43-7.76 13.25-8.92 17.47-4.3 3.32 3.63 5.46 10.58 6.82 20.24.73 5.17.94 7.74 1.58 17.38.25 3.75.17 5.32-.92 18.03h-1c1.09-12.7 1.17-14.28.92-17.97-.64-9.6-.85-12.16-1.57-17.3-1.33-9.47-3.43-16.27-6.56-19.7-3.76-4.11-8.93-3.08-16 4.32-5.65 5.9-10.54 9.5-15.25 11.5-3.58 1.53-6.13 1.99-11.6 2.44-8.8.72-13.17 2.18-20.2 8.1-5.9 4.96-19.5 18.7-21 19.99-6.52 5.68-12.47 8.12-21.32 8.12-19.78 0-29.5-4.42-34.46-16.3-2.49-5.97-9.71-14.38-16.2-18.79-3.42-2.32-6.36-3.35-8.4-2.86-2.2.53-3.44 2.92-3.44 7.45 0 4.28-.16 7.47-.5 11.31l-.23 2.56c-.09.93-.14 1.65-.19 2.35-.24 4.08.06 8.18 1.39 14.78h-1.02zm113.75 0c2.52-3.26 8.93-11.79 10.9-14.3 5.48-6.98 13.05-12.38 19.4-13.94 7.01-1.71 11.5 1.45 11.5 9.24 0 4.02-.04 5.16-.74 19h-1c.7-13.85.74-15 .74-19 0-7.12-3.86-9.83-10.26-8.26-6.11 1.5-13.5 6.77-18.85 13.57-1.86 2.36-7.65 10.07-10.43 13.69h-1.26zm-9.86-338.96c3.44 2.71 7 5.1 11.44 7.75 1.06.64 8.42 4.9 10.35 6.1 11.27 7 15 13.35 12.35 25.33-1.45 6.52-4.53 11.1-9.39 14.44-3.83 2.63-8.07 4.26-16.08 6.56-11.97 3.45-13.68 3.99-18.82 6.28a60.18 60.18 0 0 0-7.81 4.18c-11.11 7.07-19.1 7.7-27.96 3.28-3.56-1.77-17.2-11-17.2-11.01a101.77 101.77 0 0 0-5.2-3.07c-16.04-8.83-34.27-24.16-34.52-31.85-.11-3.46 1.99-6.57 6.28-10.26 1.03-.9 2.18-1.81 3.68-2.95.72-.55 3.38-2.56 3.94-3 4.47-3.4 7.18-5.79 9.32-8.45 11.12-13.82 26.55-28.68 34.36-32.28 12.06-5.54 19.84-5.77 27.37.12 3.25 2.54 5.65 6.54 8.58 13.35.29.65 2.3 5.45 2.88 6.74 1.62 3.65 2.9 5.8 4.24 6.94.72.6 1.45 1.2 2.2 1.8zm-3.49-.28c-1.63-1.39-3.03-3.74-4.77-7.65-.58-1.3-2.6-6.12-2.88-6.76-2.81-6.5-5.08-10.3-7.98-12.56-6.83-5.35-13.85-5.15-25.3.12-7.45 3.42-22.7 18.12-33.64 31.72-2.27 2.82-5.08 5.3-9.67 8.79l-3.94 2.98a79.98 79.98 0 0 0-3.59 2.88c-3.87 3.33-5.67 6-5.58 8.69.21 6.64 18.14 21.72 33.48 30.15 1.76.97 3.5 2 5.3 3.13.12.08 13.61 9.22 17.03 10.92 8.22 4.1 15.46 3.52 26-3.18a62.17 62.17 0 0 1 8.07-4.31c5.25-2.35 7-2.9 19.08-6.38 7.8-2.24 11.9-3.82 15.5-6.3 4.44-3.04 7.23-7.18 8.56-13.22 2.44-11.02-.83-16.6-11.45-23.2-1.9-1.18-9.23-5.42-10.32-6.08-4.5-2.69-8.13-5.12-11.64-7.9-.77-.6-1.52-1.21-2.26-1.84zM87.72 241.6c4.3-2.98 7.88-5 12.14-6.95.84-.4 1.73-.78 2.78-1.24l4.37-1.88a164.3 164.3 0 0 0 17.74-8.96 320.67 320.67 0 0 1 27.87-14.5c4.22-1.95 21.89-9.84 21.17-9.52 19.17-8.62 28.1-6.93 49.5 8.05 7.91 5.54 13.24 13.25 16.45 22.66 3.02 8.83 3.76 16.51 3.76 27.75 0 8.32-.66 12.95-3.68 18.97-4.18 8.36-12.3 16.14-25.58 23.47-24.45 13.49-38.83 27.55-52.83 47.84-8.83 12.8-47.76 44.21-65.16 54.15C75.04 413.55 48.89 423.5 31 423.5c-10.05 0-14.67-4.78-14.76-13.37-.07-6.32 2.06-13.73 6.3-24.32 2.95-7.37 2.02-12.9-2.16-22.29-3.19-7.17-3.88-9.14-3.88-12.52 0-3.35 1.87-6.9 5.52-11.07 2.61-3 3.5-3.83 11.9-11.5 5.09-4.66 8.08-7.6 10.7-10.75 9.46-11.36 12.62-19.47 17.9-44.78 3.12-15.05 6.63-20.28 15.12-25.25.8-.47 3.95-2.25 4.7-2.68a76.66 76.66 0 0 0 5.38-3.38zm.56.82a77.63 77.63 0 0 1-5.44 3.43l-4.7 2.67c-8.23 4.82-11.57 9.81-14.65 24.6-5.3 25.45-8.51 33.7-18.1 45.21-2.66 3.19-5.68 6.16-10.8 10.84-8.36 7.64-9.24 8.48-11.82 11.42-3.5 4.01-5.27 7.36-5.27 10.42 0 3.18.68 5.1 3.8 12.12 4.27 9.6 5.24 15.37 2.16 23.07-4.18 10.47-6.29 17.78-6.22 23.93.08 8.06 4.26 12.38 13.76 12.38 17.67 0 43.68-9.9 64.75-21.93 17.28-9.88 56.1-41.2 64.84-53.85 14.08-20.42 28.57-34.59 53.17-48.16 13.12-7.23 21.09-14.87 25.17-23.03 2.92-5.86 3.57-10.35 3.57-18.53 0-11.13-.74-18.73-3.7-27.43-3.15-9.22-8.36-16.75-16.09-22.16-21.13-14.8-29.7-16.42-48.5-7.95.7-.32-16.96 7.56-21.17 9.5-1.7.8-3.3 1.55-4.86 2.3a319.68 319.68 0 0 0-22.93 12.17 165.3 165.3 0 0 1-17.85 9.01l-4.37 1.88c-1.04.45-1.92.84-2.76 1.23a74.56 74.56 0 0 0-11.99 6.86zm-7.6 12.2c7.7-6.25 12.3-8.17 23.68-11.27 6.12-1.67 9.12-2.95 12.31-5.72 3.8-3.3 7.47-4.52 15.86-6.1 2.75-.52 3.67-.7 5.06-1.02 5.48-1.24 9.48-2.93 13.1-5.89 10.42-8.53 25.4-14.11 36.31-14.11 5.33 0 16.77 7.58 25.74 17.16 10.73 11.46 15.96 23.27 12.73 32.5-3.18 9.1-11.39 18.57-23.03 27.86-8.44 6.73-18.36 13-25.22 16.43-3.72 1.86-6.59 4.88-9.77 9.99-.69 1.1-11.1 20.25-16.03 27.83-5.62 8.65-15.4 17.36-30.23 27.96a552.58 552.58 0 0 1-9.2 6.42c-.13.09-6.81 4.65-8.6 5.89-6.47 4.46-10.35 7.35-13.05 9.83-11.64 10.67-37.14 15.54-43.7 8.98-1.96-1.96-2.2-4.06-1.95-10.52.37-9.42-.5-14.5-4.95-20.51a34.09 34.09 0 0 0-7.04-6.92c-3.93-2.95-6.07-6.11-6.56-9.49-.97-6.61 3.87-13.06 14.17-21.69 1.58-1.32 6.67-5.44 7.09-5.78a48.03 48.03 0 0 0 5.23-4.77c4.1-4.63 5.85-9.55 7.8-20.07a501.52 501.52 0 0 0 .8-4.37c.33-1.87.6-3.3.88-4.73.74-3.78 1.5-7.18 2.4-10.63 1-3.78 1.38-5.5 2.36-10.37.6-3.02.93-4.21 1.56-5.47 1.22-2.45 1.27-2.5 12.25-11.42zm.64.78c-10.77 8.74-10.88 8.84-12 11.08-.58 1.16-.88 2.3-1.47 5.22-.98 4.89-1.36 6.63-2.37 10.44-.9 3.43-1.65 6.8-2.39 10.56a339.79 339.79 0 0 0-1.29 6.95l-.39 2.15c-1.98 10.68-3.77 15.74-8.04 20.54a48.77 48.77 0 0 1-5.34 4.88c-.42.34-5.5 4.47-7.07 5.78-10.04 8.4-14.72 14.65-13.83 20.78.45 3.1 2.44 6.03 6.17 8.83 3 2.25 5.39 4.62 7.24 7.12 4.63 6.24 5.52 11.52 5.15 21.15-.25 6.14-.01 8.1 1.66 9.78 6.1 6.1 31.02 1.33 42.31-9.02 2.75-2.52 6.66-5.43 13.16-9.92l8.6-5.89c3.63-2.48 6.45-4.44 9.19-6.4 14.73-10.54 24.44-19.18 29.97-27.7 4.9-7.54 15.31-26.68 16.02-27.8 3.27-5.26 6.26-8.41 10.18-10.37 6.79-3.4 16.65-9.63 25.03-16.32 11.52-9.18 19.61-18.53 22.72-27.4 3.07-8.78-2.02-20.27-12.52-31.49-8.8-9.4-20.04-16.84-25.01-16.84-10.67 0-25.43 5.5-35.68 13.89-3.76 3.07-7.9 4.81-13.5 6.09-1.41.32-2.35.5-5.11 1.02-8.21 1.55-11.76 2.73-15.38 5.88-3.34 2.9-6.45 4.22-12.7 5.92-11.26 3.07-15.75 4.94-23.31 11.09zM212 251.85c0 7.56-.6 10.92-2.6 14.3-1.1 1.84-7.66 10.05-8.6 11.3-5.96 7.94-9.33 10.28-17.26 13.76-1.34.58-2.2 1-3.03 1.5-.55.33-1.2.66-2 1.02-.71.33-4.46 1.9-5.52 2.39-6.05 2.78-8.99 5.8-8.99 10.73 0 10.97-18.95 36.12-34.51 44.87-8.18 4.6-21.3 9.36-32.78 11.86-13.33 2.9-22.49 2.48-24.62-2.32-1.32-2.97-4.4-4.26-11.98-5.81l-.6-.12c-4.84-.99-6.94-1.55-9.03-2.64-2.92-1.5-4.48-3.7-4.48-6.84 0-2.74 1.08-5.77 3.25-9.67.85-1.53 1.82-3.13 3.23-5.35-.16.25 2.83-4.4 3.67-5.76 6.69-10.7 9.85-18.5 9.85-27.22 0-18.41 11.22-33.37 27.5-42.86 5.22-3.05 9.23-3.31 15.2-2.12 5.04 1 6.05.9 7.43-1.52 4.5-7.85 7.04-9.5 15.87-9.5 3.93 0 6.97-.98 10.47-3.16 1.56-.97 8.67-6.17 10.99-7.68 9.2-5.98 11.34-7 25.2-11.95 6.95-2.48 15.18 1.28 22.33 9.12 6.55 7.19 11.01 16.61 11.01 23.67zm-2 0c0-6.5-4.25-15.48-10.49-22.32-6.67-7.32-14.16-10.74-20.17-8.59-13.73 4.9-15.73 5.85-24.8 11.75-2.24 1.46-9.37 6.68-11.01 7.7-3.8 2.36-7.2 3.46-11.53 3.46-8.08 0-9.98 1.23-14.13 8.5-1.1 1.91-2.51 2.88-4.35 3.09-1.3.14-1.9.05-5.22-.61-5.53-1.1-9.07-.88-13.8 1.88-15.72 9.17-26.5 23.55-26.5 41.14 0 9.2-3.28 17.29-10.15 28.28l-3.68 5.77c-1.39 2.19-2.35 3.77-3.17 5.25-2.02 3.63-3 6.38-3 8.7 0 4.19 2.87 5.67 11.9 7.52l.61.12c8.27 1.7 11.7 3.13 13.4 6.95 3.17 7.14 36 0 54.6-10.46 14.98-8.43 33.49-32.99 33.49-43.13 0-5.9 3.47-9.48 10.16-12.55 1.1-.5 4.85-2.08 5.52-2.38.74-.34 1.32-.64 1.8-.93.92-.55 1.85-1 3.25-1.62 7.65-3.35 10.75-5.5 16.47-13.12 1.02-1.36 7.47-9.42 8.47-11.11 1.79-3.01 2.33-6.06 2.33-13.3zm-37.18-22.4c.15-.1 2.4-1.51 2.95-1.84.96-.57 1.7-.94 2.43-1.17 2.57-.83 5.06-.1 11.04 3.12 14.86 8 19.43 22.87 9.18 38.71-4.04 6.24-9.37 9-18.72 11.11-.85.2-1.2.27-3.13.68-6.04 1.29-8.78 2.08-11.6 3.65-3.63 2.02-6.09 4.98-7.5 9.44-7.87 24.93-19.72 43.34-36.28 50.31-16.45 6.93-21.13 8.53-27.98 8.89-4.94.25-9.8-.65-15.4-2.89a44.45 44.45 0 0 1-5.64-2.6c-4.02-2.33-5.14-4.74-4.5-9.31.3-2.13 3.77-15.53 4.84-20.65.63-3.05 1.19-6.14 1.75-9.69a464.04 464.04 0 0 0 1.35-8.9c1.42-9.41 2.5-14.27 4.49-18.65 2.46-5.43 6.13-9.03 11.72-11.13 6.59-2.47 10.54-3.1 18.03-3.53 4.75-.27 6.68-.64 9-2.05.61-.37 1.22-.81 1.82-1.33a30.61 30.61 0 0 0 3.37-3.4c.59-.69 2.38-2.9 2.63-3.19 3.36-4 6.3-5.53 12.33-5.53 3.94 0 5.9-.92 8.18-3.36-.17.18 2.75-3.14 3.85-4.22a30.95 30.95 0 0 1 6.79-5c1.5-.83 3.15-1.62 4.99-2.38a64.92 64.92 0 0 0 10.01-5.1zm-14.52 8.34a29.95 29.95 0 0 0-6.57 4.84 116.68 116.68 0 0 0-3.82 4.2c-2.46 2.63-4.68 3.67-8.91 3.67-5.72 0-8.39 1.39-11.57 5.17-.23.28-2.03 2.5-2.63 3.2a31.6 31.6 0 0 1-3.47 3.51c-.65.55-1.3 1.03-1.96 1.43-2.5 1.51-4.55 1.9-9.47 2.19-7.39.42-11.25 1.04-17.72 3.47-5.34 2-8.82 5.4-11.17 10.6-1.93 4.27-3 9.07-4.41 18.39l-.65 4.34-.7 4.57c-.57 3.56-1.12 6.67-1.76 9.73-1.08 5.18-4.54 18.53-4.83 20.59-.59 4.17.35 6.18 4.01 8.3 1.35.77 3.1 1.58 5.52 2.55 5.46 2.18 10.18 3.05 14.97 2.8 6.69-.34 11.32-1.93 27.65-8.8 16.21-6.83 27.92-25.01 35.71-49.7 1.49-4.7 4.12-7.86 7.97-10 2.93-1.63 5.74-2.45 11.87-3.76 1.92-.4 2.28-.49 3.12-.68 9.12-2.06 14.24-4.7 18.1-10.67 9.92-15.34 5.55-29.55-8.82-37.29-5.75-3.1-8.03-3.76-10.25-3.05-.65.2-1.33.54-2.23 1.08-.55.32-2.77 1.72-2.93 1.82a65.91 65.91 0 0 1-10.16 5.17c-1.8.75-3.42 1.52-4.89 2.33zm-42.39 32.72c16.15-2.87 26.36-.97 32.47 6.16 5.08 5.93 1.13 21.42-5.93 35.55-4.79 9.58-10.6 16.21-23.16 25.19-14.15 10.1-35.5 12.2-40.71 3.85-1.86-2.97-2.1-8.14-1.06-15.73.78-5.68 1.86-10.71 4.73-22.98l.12-.51c1.59-6.8 2.37-10.31 3.14-14.14 1.45-7.25 3.74-11.47 7.26-13.74 2.81-1.8 5.53-2.28 12.33-2.62 5.33-.27 7.56-.46 10.81-1.03zm.18.98c-3.3.59-5.56.78-10.94 1.05-6.62.33-9.23.78-11.84 2.46-3.25 2.1-5.42 6.09-6.82 13.1-.77 3.84-1.56 7.35-3.15 14.17l-.12.5c-2.86 12.24-3.93 17.26-4.7 22.9-1.03 7.36-.79 12.36.9 15.07 4.82 7.7 25.54 5.67 39.29-4.15 12.43-8.88 18.13-15.39 22.84-24.81 6.86-13.72 10.75-29 6.07-34.45-5.84-6.81-15.7-8.65-31.53-5.84zM132 276.5c7.12 0 10.66 3.08 11.25 8.7.42 4.02-.43 8.14-2.77 15.94-2.56 8.52-18.36 25.38-27.2 31.28-7.01 4.67-20.02 5.67-26.57.99-3.99-2.85-3.53-12.08.02-26.46.68-2.75 1.47-5.65 2.37-8.76a412.6 412.6 0 0 1 3.05-10.14l.37-1.2c1.48-4.8 5.1-7.75 10.73-9.27 4.4-1.2 9.54-1.5 17.48-1.33l3.89.1c3.87.11 5.42.15 7.38.15zm0 1c-1.97 0-3.53-.04-7.41-.15l-3.88-.1c-7.85-.17-12.92.13-17.2 1.3-5.32 1.43-8.67 4.16-10.03 8.6a1277.83 1277.83 0 0 1-1.6 5.21c-.68 2.2-1.27 4.17-1.82 6.1-.9 3.1-1.68 5.99-2.36 8.73-3.43 13.88-3.87 22.93-.4 25.4 6.17 4.42 18.73 3.45 25.42-1 8.66-5.78 24.33-22.49 26.8-30.73 2.3-7.67 3.14-11.71 2.73-15.56-.53-5.1-3.64-7.8-10.25-7.8zm-17.79 7a31.3 31.3 0 0 1 8.57 1.4c5.42 1.78 8.72 5.03 8.72 10.1 0 9.59-9.51 17.2-22.34 21.47-9.82 3.28-13.62-1.79-11.66-16.54.84-6.28 3.82-10.67 8.24-13.46a20.38 20.38 0 0 1 8.47-2.97zm-.6 1.08a19.39 19.39 0 0 0-7.34 2.73c-4.18 2.64-6.98 6.78-7.77 12.76-1.89 14.11 1.36 18.45 10.34 15.46C121.3 312.37 130.5 305 130.5 296c0-4.56-2.98-7.5-8.03-9.15a28.05 28.05 0 0 0-8.2-1.35c-.13 0-.35.03-.66.08zm80.87-23.45c-2.72 9.8-14.93 9.86-26.72 3.3-10.17-5.64-13.8-17.98-5-22.87a66.53 66.53 0 0 0 4.48-2.7l2.03-1.3a50.15 50.15 0 0 1 3.92-2.3c4.73-2.43 8.82-2.8 14-.72 9.16 3.66 10.98 13.33 7.3 26.6zm-20.83-24.98a49.26 49.26 0 0 0-3.84 2.25l-2.03 1.3c-.84.53-1.5.95-2.16 1.35-.82.5-1.6.96-2.38 1.39-7.94 4.4-4.59 15.8 5 21.12 11.31 6.29 22.8 6.23 25.28-2.7 3.57-12.83 1.85-21.97-6.7-25.4-4.9-1.95-8.69-1.62-13.17.7zm17.85 12.15c0 5.7-2.44 9-6.64 9.96-3.3.76-7.56-.05-11.08-1.81l-1.89-.94c-.67-.34-1.18-.62-1.63-.88-4.07-2.38-4.13-4.97.34-10.93 6.8-9.06 20.9-7.16 20.9 4.6zm-1 0c0-5.3-2.87-8.55-7.32-9.16-4.23-.57-8.99 1.44-11.78 5.16-4.15 5.54-4.1 7.44-.64 9.47.44.25.93.51 1.59.85l1.87.93c3.34 1.67 7.36 2.44 10.42 1.74 3.73-.86 5.86-3.74 5.86-9zM387 530.3c0-12.8 2.44-16.74 18.48-29.77a56.8 56.8 0 0 1 7.61-5.2c2.6-1.5 5.33-2.82 8.5-4.18 1.24-.53 2.48-1.05 4.1-1.7l3.92-1.57c9.4-3.83 13.74-6.7 16.62-12.05 1.2-2.22 2.21-4.4 3.23-6.83a148.57 148.57 0 0 0 1.54-3.84l.3-.74.56-1.44c3.2-8.02 6.05-12.08 12.7-16.5a35.26 35.26 0 0 0 4.96-4 46.36 46.36 0 0 0 3.88-4.29c.27-.34 2.55-3.2 3.2-3.98 3.48-4.15 6.51-5.9 11.51-5.9 3.08 0 5.62-.63 9.57-2.1 5.42-2.02 6.53-2.34 8.96-2.2 2.53.13 4.85 1.26 7.18 3.59 1.3 1.3 5.55 5.83 6.52 6.78 5.06 5 9.44 6.92 17.77 6.92a197.5 197.5 0 0 1 12.08.45c15.93.87 21.94.57 25.28-2.21 6.91-5.77 11.64-2.73 11.64 7.76 0 10.73-8.6 20-19 20-4.8 0-8.32 1.43-9.34 3.67-1.12 2.48.68 6.15 5.98 10.57 13.6 11.33 11.24 20.76-7.64 20.76a21.91 21.91 0 0 0-14.6 5.24c-3.28 2.71-5.8 5.86-9.85 11.82l-1.52 2.25c-3.1 4.57-5.01 7.1-7.32 9.4-6.21 6.21-9.3 7.64-13.05 6.89l-1-.23a10.82 10.82 0 0 0-2.66-.37c-1.6 0-2.41.67-8.18 6.22-4.85 4.67-8.07 6.78-11.82 6.78-1.33 0-3.46 1.15-6.45 3.45-1.27.98-2.68 2.14-4.5 3.7l-4.92 4.29a181.11 181.11 0 0 1-4.54 3.82c-9.33 7.56-15.63 10.2-20.21 6.52-2.7-2.15-4.14-4.51-4.63-7.26-.37-2.04-.26-3.63.29-7.3.87-5.85.65-8.42-1.83-11.6-2.32-2.98-2.96-3.22-3.77-2.39-.25.26-1.35 1.63-1.61 1.94-2.21 2.5-4.85 3.57-9 2.82-4.6-.84-5.57-4.11-4.72-10.09l.24-1.56c.6-3.66.68-4.93.25-5.8-.44-.86-1.9-.94-5.23.4l-.74.29c-13.78 5.54-15.26 6.09-19.43 6.67-6.03.84-9.31-1.6-9.31-7.9zm2 0c0 5 2.14 6.6 7.04 5.92 3.91-.55 5.43-1.1 18.95-6.55l.75-.3c4.17-1.66 6.7-1.54 7.76.58.71 1.43.62 2.76-.06 7l-.24 1.53c-.72 5.04-.06 7.27 3.09 7.84 3.43.62 5.38-.17 7.15-2.18.2-.23 1.34-1.66 1.68-2 1.9-1.96 3.82-1.25 6.78 2.55 2.9 3.74 3.17 6.77 2.22 13.12-1 6.75-.52 9.4 3.62 12.71 3.49 2.8 9.1.45 17.7-6.51 1.35-1.1 2.75-2.28 4.49-3.78l4.93-4.3c1.84-1.58 3.27-2.76 4.58-3.77 3.34-2.56 5.74-3.86 7.67-3.86 3.04 0 5.95-1.9 10.43-6.22l2.46-2.39c.94-.89 1.67-1.56 2.37-2.13 1.81-1.49 3.3-2.26 4.74-2.26 1.03 0 1.81.13 3.1.42.7.16.71.17.96.21 2.96.6 5.45-.55 11.23-6.33 2.2-2.2 4.06-4.65 7.09-9.11l1.52-2.25c4.15-6.11 6.76-9.37 10.22-12.24a23.9 23.9 0 0 1 15.88-5.7c16.87 0 18.62-7.01 6.36-17.23-5.9-4.92-8.12-9.41-6.52-12.93 1.42-3.12 5.67-4.84 11.16-4.84 9.25 0 17-8.34 17-18 0-8.94-2.88-10.79-8.36-6.23-3.94 3.28-9.98 3.59-26.67 2.68l-1.02-.06c-5.09-.27-7.99-.39-10.95-.39-8.88 0-13.76-2.14-19.18-7.5-1-.98-5.26-5.53-6.53-6.79-1.99-1.99-3.86-2.9-5.87-3-2.03-.12-3.06.18-8.15 2.07-4.15 1.55-6.9 2.22-10.27 2.22-4.33 0-6.84 1.46-9.98 5.2-.63.74-2.89 3.6-3.18 3.95a48.29 48.29 0 0 1-4.04 4.46 37.26 37.26 0 0 1-5.24 4.23c-6.26 4.17-8.9 7.91-11.95 15.58l-.57 1.43-.28.74a531.5 531.5 0 0 1-1.56 3.88 77.49 77.49 0 0 1-3.32 7c-3.16 5.88-7.82 8.97-17.63 12.96l-3.92 1.58c-1.6.64-2.84 1.15-4.05 1.67a79.2 79.2 0 0 0-8.3 4.08 54.8 54.8 0 0 0-7.35 5.02C391.12 514.78 389 518.21 389 530.31zm133.22-79.76c3.06 1.53 6.54 2.02 10.68 1.7 2.53-.2 4.91-.62 8.8-1.49 5.36-1.19 6.33-1.38 8.33-1.54 2.78-.23 4.82.17 6.29 1.4 1.58 1.31 1.96 2.72 1.26 4.22-.66 1.38-1.05 1.74-5.05 5.07-3.53 2.93-5.03 4.83-5.03 7.09 0 7.3 1.29 10.02 7.83 15.62 3.86 3.3 5.93 6.84 5.28 9.62-.75 3.25-4.96 5.02-12.61 5.02-7.18 0-12.7 4.61-20.03 14.68-.5.7-3.96 5.57-4.94 6.87a38.89 38.89 0 0 1-4.72 5.5c-1.06.98-2.09 1.7-3.1 2.15-2.85 1.26-5.05 1.57-9.83 1.74-7.66.27-10.87 1.45-14.98 7.1-1.58 2.17-3.11 4-4.68 5.6a42.87 42.87 0 0 1-8.65 6.69c-.15.08-10.69 6.19-14.8 8.83-3.76 2.42-6.45 2.04-8.22-.77-1.28-2.03-1.9-4.54-2.87-10.35-.84-5.08-1.27-7.08-2.06-8.93-.97-2.3-2.21-3.24-4.02-2.88-6.2 1.24-8.95 1.39-10.98.2-2.37-1.4-3.13-4.62-2.62-10.73.16-1.96-1.04-2.87-3.76-3.04-2.24-.13-4.9.2-9.94 1.12l-.69.12c-7.97 1.45-10.72 1.72-12.72.73-2.91-1.43-1.6-5.27 4.23-12.21 5.48-6.53 10.6-10.81 15.76-13.53 3.74-1.97 5.94-2.65 12.16-4.1 7.29-1.72 10.4-3.51 14.04-9.31 2.96-4.75 10.74-18.62 12.14-20.84 3.59-5.67 6.8-9.1 11.05-11.34 2.6-1.38 4.72-2.82 9.17-6.07l1.38-1.01c7.85-5.72 12.3-7.98 17.68-7.98 4.22 0 6.49 1.36 9.13 4.77.34.43 1.67 2.22 2 2.67.85 1.09 1.6 1.98 2.45 2.83a24.29 24.29 0 0 0 6.64 4.78zm-.44.9c-2.8-1.4-5-3.03-6.92-4.97-.87-.9-1.65-1.81-2.51-2.93-.35-.46-1.68-2.25-2.01-2.67-2.47-3.18-4.46-4.38-8.34-4.38-5.09 0-9.4 2.2-17.09 7.78l-1.38 1.01c-4.49 3.29-6.63 4.74-9.3 6.15-4.06 2.15-7.16 5.45-10.66 11-1.39 2.19-9.16 16.05-12.15 20.82-3.79 6.07-7.13 7.98-14.66 9.75-6.13 1.45-8.27 2.1-11.92 4.02-5.04 2.66-10.05 6.86-15.46 13.3-5.43 6.46-6.53 9.69-4.55 10.66 1.7.84 4.48.57 12.1-.81l.7-.13c5.12-.93 7.82-1.27 10.17-1.12 3.21.2 4.92 1.48 4.7 4.11-.48 5.76.2 8.64 2.13 9.78 1.73 1.02 4.34.88 10.27-.31 2.35-.47 4 .78 5.14 3.47.83 1.95 1.27 4 2.07 8.8l.06.36c.94 5.65 1.55 8.11 2.72 9.98 1.46 2.3 3.52 2.6 6.84.46 4.14-2.66 14.69-8.77 14.81-8.85a41.9 41.9 0 0 0 8.46-6.54 47.89 47.89 0 0 0 4.6-5.48c4.32-5.95 7.81-7.23 15.74-7.5 4.66-.17 6.76-.47 9.46-1.67.9-.4 1.85-1.06 2.84-1.96a38.03 38.03 0 0 0 4.6-5.36c.96-1.3 4.4-6.16 4.93-6.87 7.5-10.31 13.22-15.09 20.83-15.09 7.24 0 11.02-1.6 11.64-4.24.54-2.32-1.36-5.55-4.97-8.64-6.75-5.79-8.17-8.79-8.17-16.38 0-2.67 1.64-4.74 5.39-7.86 3.8-3.17 4.23-3.56 4.78-4.73.5-1.06.25-1.99-.99-3.03-2.23-1.85-4.72-1.65-13.76.36-3.93.87-6.35 1.3-8.94 1.5-4.3.34-7.97-.18-11.2-1.8zm-28-3.9c5.65-2.82 8.96-2.2 12.9 1.37.56.5 2.6 2.47 3.02 2.87 4.2 3.89 8.07 5.71 14.3 5.71 11.37 0 14 1.41 16.1 8.09.26.83 1.35 4.6 1.66 5.62.8 2.63 1.64 5.03 2.7 7.6 2.13 5.17 2.64 8.32 1.72 10.24-.77 1.61-2.1 2.18-5.37 2.79-2.32.43-2.8.53-3.85.85-1.85.58-3.35 1.4-4.6 2.66-1 1-2.02 2.13-3.31 3.66-.6.71-2.91 3.5-3.46 4.14-7.2 8.54-12.43 12.35-19.59 12.35-3.76 0-6.95 1.28-10.59 4-1.84 1.37-11.62 10.31-15.22 13.06a73.09 73.09 0 0 1-8.95 5.88c-4.58 2.54-7.35 3.22-8.98 2.23-1.32-.8-1.65-2.07-1.94-5.5a52.53 52.53 0 0 0-.16-1.81c-.54-4.73-2.24-6.86-7.16-6.86-7.11 0-8.85-1.23-9.73-5.41-.96-4.61-2.1-6.7-6.55-9.67-3.97-2.65-4.31-5.42-1.52-8.22 2-2 4.63-3.5 11.35-6.87 6.61-3.3 9.2-4.8 11.1-6.68a39.09 39.09 0 0 0 5.3-6.48c.98-1.5 1.83-3.04 2.88-5.13l2.12-4.3c.91-1.83 1.72-3.37 2.61-4.98 5.74-10.32 10.37-14.78 23.22-21.2zm-22.34 21.7c-.89 1.59-1.69 3.12-2.6 4.94l-2.11 4.3a52.9 52.9 0 0 1-2.94 5.23 40.08 40.08 0 0 1-5.44 6.63c-2 2-4.62 3.51-11.35 6.87-6.6 3.3-9.2 4.8-11.1 6.69-2.33 2.34-2.08 4.37 1.38 6.67 4.7 3.14 5.96 5.46 6.97 10.3.78 3.7 2.09 4.62 8.75 4.62 5.5 0 7.57 2.57 8.15 7.75.06.5.09.82.17 1.84.25 3.06.55 4.17 1.46 4.72 1.2.74 3.69.13 7.98-2.25a72.09 72.09 0 0 0 8.82-5.8c3.55-2.7 13.34-11.65 15.24-13.07 3.79-2.83 7.18-4.19 11.18-4.19 6.77 0 11.8-3.67 18.83-12l3.45-4.13a60.07 60.07 0 0 1 3.37-3.72 11.72 11.72 0 0 1 5.01-2.91c1.1-.34 1.6-.45 3.97-.89 2.95-.55 4.07-1.02 4.65-2.23.76-1.59.28-4.5-1.74-9.43a84.46 84.46 0 0 1-2.74-7.69c-.31-1.03-1.4-4.8-1.66-5.61-1.95-6.2-4.16-7.39-15.14-7.39-6.5 0-10.61-1.93-14.98-5.98-.44-.4-2.46-2.37-3.01-2.86-3.65-3.3-6.52-3.85-11.79-1.21-12.67 6.33-17.15 10.65-22.78 20.8zm55.86 11.93c-2.98 6.45-16.78 15.26-26.74 15.26-5.33 0-7.56-2.98-7.11-7.86.32-3.48 2.1-7.91 3.93-10.61l1.52-2.32a44.95 44.95 0 0 1 1.88-2.7c3.66-4.8 7.85-7.45 13.62-7.45 9.06 0 15.75 9.52 12.9 15.68zm-.9-.42c2.52-5.47-3.65-14.26-12-14.26-5.4 0-9.33 2.48-12.82 7.06-.6.8-1.17 1.6-1.85 2.64 0 0-1.2 1.87-1.52 2.33-1.74 2.57-3.46 6.85-3.77 10.14-.4 4.33 1.43 6.77 6.12 6.77 9.57 0 23.02-8.58 25.83-14.68zm-69.67 20.74c2.08.18 4.44.81 5.88 1.8 2.12 1.47 2.2 3.6-.26 6.05-5.14 5.15-12.85 4.34-12.85-1.35 0-4.66 3.14-6.84 7.23-6.5zm-.09 1c-3.56-.3-6.14 1.5-6.14 5.5 0 4.58 6.53 5.26 11.15.65 2.03-2.04 1.98-3.43.4-4.52-1.27-.88-3.48-1.47-5.4-1.63zm29.59-225.95c4.64 2.35 17.27 8.24 19.39 9.43a24.14 24.14 0 0 1 7.05 5.64 45.03 45.03 0 0 1 3.75 5.2c2.4 3.78.04 7.66-6.2 11.63-4.97 3.16-12.18 6.3-21.95 9.82-4.84 1.74-19.63 6.68-21.1 7.2-6.59 2.33-14.85.1-25.14-5.86-3.93-2.27-8-5-12.94-8.54-2.23-1.61-9.5-6.99-10.7-7.85a81.21 81.21 0 0 0-8.63-5.7c-4.82-2.6-4.45-6.64.17-12.13 3.27-3.88 4.17-4.67 18.1-16.33a230.2 230.2 0 0 0 8.89-7.74 95.2 95.2 0 0 0 4.72-4.66c5.08-5.43 9.8-6.49 14.97-3.92 2.24 1.1 4.53 2.85 7.43 5.52 1.48 1.37 6.94 6.72 7.98 7.7 5.2 4.91 9.46 8.2 14.2 10.6zm-.46.9c-4.85-2.45-9.18-5.79-14.44-10.76-1.05-1-6.5-6.34-7.97-7.69-2.83-2.61-5.06-4.3-7.2-5.37-4.75-2.36-9-1.4-13.8 3.71a96.18 96.18 0 0 1-4.76 4.71c-2.48 2.3-5.16 4.62-8.92 7.77-13.86 11.6-14.77 12.4-17.98 16.21-4.28 5.08-4.58 8.4-.46 10.61 2.23 1.2 4.9 2.99 8.74 5.77 1.2.87 8.47 6.24 10.7 7.85a154.8 154.8 0 0 0 12.85 8.49c10.06 5.82 18.07 7.98 24.3 5.78 1.48-.52 16.27-5.47 21.1-7.2 9.7-3.5 16.86-6.61 21.75-9.72 5.84-3.71 7.9-7.1 5.9-10.26a44.09 44.09 0 0 0-3.67-5.08 23.16 23.16 0 0 0-6.78-5.42c-2.08-1.16-14.68-7.05-19.36-9.4zm-38.83 8.05c3.11-.37 5.7-.13 8.4.7 2.15.66 2.74.93 8.64 3.77 4.75 2.29 8.39 3.86 13.19 5.56 8.38 2.97 11.32 6.23 8.83 9.76-2.08 2.94-8.04 5.92-17.84 9.18-8.45 2.82-15.48 2.35-21.43-.9-4.65-2.55-8.33-6.5-12.15-12.3-2.9-4.41-2.73-8.2.16-11.06 2.48-2.45 6.87-4.07 12.2-4.7zm.12 1c-5.13.6-9.33 2.16-11.62 4.42-2.53 2.5-2.68 5.77-.02 9.8 3.73 5.68 7.3 9.51 11.8 11.97 5.7 3.11 12.43 3.57 20.62.84 9.59-3.2 15.44-6.12 17.34-8.82 1.94-2.75-.5-5.45-8.35-8.24-4.84-1.72-8.5-3.3-13.28-5.6-5.84-2.81-6.42-3.07-8.5-3.71a18.42 18.42 0 0 0-8-.66zM202.5 500.38c0 4.78-1.45 7.56-4.43 8.93-2.29 1.05-4.55 1.23-10.79 1.2l-1.78-.01c-9.19 0-17-7.65-17-15.5 0-7.59 10.6-10.51 19.74-5.44 2.78 1.55 4.21 1.94 8.57 2.75 4.44.83 5.69 2.27 5.69 8.07zm-1 0c0-5.3-.9-6.34-4.88-7.08-4.45-.83-5.96-1.25-8.86-2.86-8.57-4.76-18.26-2.1-18.26 4.56 0 7.3 7.36 14.5 16 14.5h1.79c6.06.04 8.26-.14 10.36-1.1 2.6-1.2 3.85-3.6 3.85-8.02zm33.33-117.85c3.71-1.31 8.7-2.7 16.1-4.55 2.58-.65 16.53-4.04 20.56-5.05 19.59-4.93 31.55-8.9 38.23-13.35 14.93-9.95 36.87-33.88 43.83-47.8 2.25-4.5 4.65-6.38 7.68-6.25 1.26.06 2.61.45 4.32 1.2a50.81 50.81 0 0 1 3.54 1.7l1.26.63c4.78 2.34 8.38 3.44 12.65 3.44 7.2 0 10.01 3.07 8.35 7.91-1.4 4.06-5.92 8.91-11.1 12.02-8.3 4.98-11.75 17.3-11.75 33.57 0 3.59-1.37 6.28-3.98 8.36-1.98 1.58-4.2 2.6-8.47 4.16l-1.02.37c-4.85 1.75-6.98 2.77-8.68 4.46-5.09 5.1-12.54 7.15-20.35 7.15-1.38 0-2.47.92-3.99 3.1-.29.41-1.32 1.95-1.47 2.18-2.68 3.92-4.93 5.72-8.54 5.72-7.84 0-10.74.93-21.76 6.94-5.18 2.82-8.8 3.58-14.66 3.68-.26 0-.47 0-.92.02-4.82.06-7.12.3-10.51 1.34a73.43 73.43 0 0 0-8.89 3.56c-2.17 1-10.53 5.01-10.23 4.87-7.79 3.7-13.32 5.98-18.9 7.57-12.41 3.55-18.58 2.24-27.42-4.07-2.58-1.85-2.72-4.43-.83-7.62 1.45-2.45 3.9-5.09 8.08-8.97l1.78-1.64c3.92-3.6 4.48-4.11 5.9-5.53 2.32-2.32 3.12-3.5 5.48-7.63 1.93-3.36 3.37-5.11 6.27-7.06 2.3-1.54 5.34-2.98 9.44-4.43zm.34.94c-4.03 1.42-7 2.83-9.22 4.32-2.75 1.85-4.1 3.49-5.96 6.73-2.4 4.2-3.24 5.44-5.64 7.83-1.43 1.44-2 1.96-5.94 5.57l-1.77 1.63c-4.1 3.82-6.52 6.41-7.9 8.75-1.65 2.79-1.54 4.8.55 6.3 8.6 6.14 14.46 7.38 26.57 3.92 5.5-1.57 11-3.84 18.74-7.51-.3.14 8.06-3.88 10.24-4.88a74.3 74.3 0 0 1 9.01-3.6c3.51-1.09 5.89-1.33 10.8-1.4h.91c5.72-.1 9.18-.83 14.2-3.57 11.16-6.08 14.2-7.06 22.24-7.06 3.19 0 5.2-1.6 7.71-5.28l1.48-2.2c1.7-2.43 3-3.52 4.81-3.52 7.57 0 14.78-2 19.65-6.85 1.83-1.84 4.04-2.9 9.04-4.7l1.02-.37c8.6-3.13 11.79-5.67 11.79-11.58 0-16.6 3.53-29.2 12.24-34.43 5-3 9.35-7.67 10.66-11.48 1.42-4.13-.83-6.59-7.4-6.59-4.45 0-8.19-1.14-13.09-3.54-7.52-3.67-6.78-3.34-8.72-3.43-2.58-.1-4.65 1.52-6.74 5.7-7.04 14.07-29.1 38.14-44.17 48.19-6.81 4.54-18.84 8.52-38.55 13.48-4.03 1.02-17.98 4.4-20.56 5.05-7.37 1.84-12.33 3.23-16 4.52zM252 387.5c2.08 0 4-.2 7.25-.69 5.22-.77 6.64-.9 8.46-.5 2.52.56 3.79 2.35 3.79 5.69 0 4.05-2.27 7.29-6.62 10.11-3.24 2.1-6.53 3.53-14.15 6.4l-.27.1-2.28.86c-3.04 1.16-5.27 2.52-9.33 5.43l-.8.57c-8.19 5.88-13.35 8.03-23.05 8.03-4.98 0-6.88-2.03-5.75-5.62.87-2.81 3.58-6.56 7.8-11.13 1.26-1.37 2.64-2.8 4.15-4.3 3.17-3.14 11.25-10.61 11.45-10.8.46-.47.93-.89 1.4-1.26 3.38-2.71 5.77-3.08 14.18-2.93 1.65.03 2.63.04 3.77.04zm0 1c-1.15 0-2.13-.01-3.79-.04-8.18-.14-10.4.2-13.54 2.71-.44.35-.88.74-1.32 1.18-.2.21-8.3 7.69-11.45 10.82a134.6 134.6 0 0 0-4.12 4.26c-4.12 4.47-6.76 8.12-7.58 10.75-.9 2.88.45 4.32 4.8 4.32 9.46 0 14.44-2.07 22.46-7.84l.8-.57c4.13-2.96 6.42-4.36 9.56-5.56l2.3-.86.25-.1c7.55-2.84 10.8-4.25 13.97-6.3 4.08-2.65 6.16-5.6 6.16-9.27 0-2.89-.97-4.26-3-4.7-1.65-.37-3.05-.25-8.1.5-3.3.5-5.26.7-7.4.7zm112.47-45.34c-1.88 5.44-1.98 6.76-.98 12.76 1.18 7.06-1.38 16.58-5.49 16.58a16.89 16.89 0 0 0-1.51.07l-.64.04c-2.86.18-4.83.17-6.94-.17-6.55-1.06-10.41-5.14-10.41-13.44 0-13.9 2.14-19.69 8.13-26.33a21.9 21.9 0 0 0 2.52-3.75c.59-1.03 2.78-5.13 2.72-5.01 4.44-8.14 7.71-11.53 12.25-10.4 1.17.3 2.2.77 3.58 1.59l1.39.84a20 20 0 0 0 3.1 1.6c.7.27 1.8.32 4.75.26l.72-.01c3.16-.05 4.78.08 5.83.66 1.61.89 1.2 2.56-1.14 4.9a215.9 215.9 0 0 1-3.86 3.76c-10.6 10.1-12.75 12.4-14.02 16.05zm-.94-.32c1.34-3.9 3.46-6.17 14.27-16.46 1.55-1.47 2.73-2.62 3.85-3.73 1.94-1.95 2.17-2.88 1.35-3.33-.82-.45-2.37-.58-5.32-.53l-.72.01c-3.14.06-4.26.02-5.14-.34-1.06-.41-1.97-.9-3.25-1.67l-1.38-.83a12.1 12.1 0 0 0-3.31-1.47c-3.88-.97-6.92 2.17-11.13 9.9.07-.13-2.14 3.98-2.73 5.02a22.71 22.71 0 0 1-2.65 3.92c-5.81 6.47-7.87 12-7.87 25.67 0 7.79 3.48 11.47 9.57 12.45 2.01.33 3.92.34 6.71.16a371.33 371.33 0 0 0 1.23-.07c.42-.03.73-.04.99-.04 3.2 0 5.6-8.9 4.5-15.42-1.02-6.16-.91-7.64 1.03-13.24zm-9.26 12.42c.58.52 2.5 1.9 2.55 1.93 1.96 1.57 2.04 3.31.01 6.36-3.74 5.64-8.83 3.09-8.83-4.55 0-3.81.51-5.67 2.07-6.02 1.18-.26 2 .3 4.2 2.28zm-1.34 1.48c-1.5-1.35-2.23-1.85-2.43-1.8-.17.03-.5 1.23-.5 4.06 0 5.87 2.67 7.21 5.17 3.45 1.5-2.26 1.47-2.84.4-3.7.03.03-1.95-1.4-2.64-2zm222.9-130.19c2.2-1.1 3.67-1.66 5.88-2.36l.28-.09a48.92 48.92 0 0 0 8.79-3.55c4.17-2.08 6.35-1.88 6.96.84.44 2 .2 4.01-1.25 12.7-2.27 13.62-9.16 26.14-21.17 36.3-4.3 3.63-7.41 4.39-9.75 2.44-1.88-1.57-3.1-4.57-4.61-10.48-.3-1.15-1.43-5.83-1.72-6.96a114.18 114.18 0 0 0-2.71-9.22c-2.4-6.82-3.03-10.78-2.1-12.94.77-1.83 2.08-2.24 5.6-2.45 1.49-.09 2.09-.14 2.97-.28l1.95-.33c.72-.12 1.22-.2 1.68-.29 1.1-.2 1.92-.38 2.71-.6 1.7-.49 3.42-1.2 6.49-2.73zm.44.9c-3.11 1.54-4.88 2.29-6.65 2.79-.84.23-1.69.42-2.81.63a108.77 108.77 0 0 1-3.81.63c-.77.13-1.39.19-2.92.28-3.13.18-4.17.51-4.74 1.85-.78 1.84-.2 5.62 2.13 12.2a115.12 115.12 0 0 1 2.74 9.31l1.72 6.96c1.46 5.7 2.62 8.58 4.28 9.96 1.87 1.56 4.49.93 8.47-2.44 11.82-10 18.6-22.3 20.83-35.7 1.4-8.45 1.65-10.51 1.25-12.31-.41-1.87-1.86-2-5.54-.16a49.87 49.87 0 0 1-8.93 3.6l-.28.1a35.4 35.4 0 0 0-5.74 2.3zm-4.5 6.58c1.37-.32 2.5-.75 3.9-1.42.35-.18 2.57-1.31 3.32-1.67 1.5-.71 2.97-1.31 4.7-1.89 2.7-.9 4.64-.77 5.88.4.98.94 1.34 2.26 1.41 4.18.02.4.02.7.02 1.37 0 5.63-4.63 16.88-11.34 22.75-4.34 3.8-7.31 4.67-9.92 2.52-2.06-1.7-3.5-4.65-6.67-12.91-1.86-4.83-2.05-8.1-.68-10.2 1.12-1.7 2.9-2.36 5.83-2.7l1.26-.12c1.19-.12 1.75-.19 2.3-.31zm-2.1 2.3l-1.22.12c-2.4.27-3.7.76-4.39 1.81-.93 1.43-.78 4.1.87 8.38 3.02 7.84 4.41 10.71 6.08 12.09 1.63 1.34 3.64.75 7.33-2.48C584.6 250.77 589 240.08 589 235c0-.64 0-.93-.02-1.29-.05-1.44-.3-2.33-.79-2.8-.6-.57-1.8-.65-3.87.04a37.95 37.95 0 0 0-4.47 1.8c-.72.34-2.93 1.47-3.32 1.66a19.54 19.54 0 0 1-4.3 1.56c-.66.16-1.28.24-2.56.36zm-227.73-88.98c-1.59 4.3-3.54 7.25-7.14 11.4l-2.6 2.97a67.02 67.02 0 0 0-2.63 3.23 46.4 46.4 0 0 0-4.68 7.5c-2.85 5.7-7.14 10.18-12.85 13.89-4.25 2.76-8.25 4.62-15.67 7.59-11.01 4.4-16.43 1.26-27.22-16.4-2.86-4.69-8.8-8.63-17.98-12.66-3-1.33-12.88-5.24-14.43-5.92-4.96-2.18-7.04-3.72-6.42-5.85.67-2.32 5.3-4.05 15.48-6.08 16.63-3.32 26.93-3.82 39.93-3.02 7.9.49 9.67.5 12.74-.26 1.99-.48 3.92-1.3 6-2.6l2.79-1.71c9.86-6.14 12.94-7.96 17.3-9.9 6.03-2.71 10.57-3.32 13.94-1.4 7.2 4.12 7.68 7.7 3.44 19.22zm-1.88-.7c3.95-10.7 3.6-13.26-2.56-16.78-2.66-1.52-6.62-.99-12.12 1.48-4.24 1.9-7.3 3.7-17.07 9.77l-2.79 1.73a22.6 22.6 0 0 1-6.57 2.84c-3.36.81-5.22.8-13.34.3-12.84-.78-22.97-.29-39.41 3-4.9.97-8.45 1.88-10.79 2.75-2.03.76-3.04 1.45-3.17 1.91-.16.57 1.48 1.79 5.3 3.46 1.5.67 11.39 4.58 14.44 5.93 9.52 4.19 15.74 8.3 18.87 13.44 10.35 16.93 14.87 19.56 24.78 15.6 7.3-2.93 11.21-4.75 15.33-7.42 5.42-3.53 9.47-7.75 12.15-13.1 1.44-2.9 3.02-5.4 4.86-7.82a68.95 68.95 0 0 1 2.72-3.33l2.6-2.97c3.46-3.99 5.28-6.75 6.77-10.79zm-6.64-.39c-7.94 12.8-18.53 21.75-33.3 25.23-7.82 1.83-12.47-.79-13.12-5.93-.55-4.45 2.29-9.06 6-9.06 3.02 0 5.6-1.68 15.38-9.16 1.47-1.12 2.57-1.96 3.66-2.74 4.4-3.2 7.77-5.17 10.82-6.08 5.57-1.67 9.33-2.15 11.35-1.22 2.5 1.14 2.22 4.13-.79 8.96zm-.84-.52c2.72-4.4 2.94-6.74 1.21-7.53-1.71-.79-5.32-.33-10.65 1.27-2.9.87-6.2 2.79-10.51 5.92-1.08.79-2.18 1.62-3.65 2.74-10.08 7.72-12.62 9.36-15.98 9.36-3.02 0-5.5 4.02-5 7.94.56 4.5 4.62 6.78 11.89 5.07 14.48-3.4 24.86-12.18 32.69-24.77zM461.17 33.53c13.88 4.96 20.75 4.96 31.62.01 3.02-1.37 5.47-2.94 11-6.82 5.57-3.92 8.05-5.51 11.14-6.92 4.14-1.88 7.78-2.38 11.22-1.28 3.92 1.26 6.2 12.3 6.78 28.45.5 14.2-.52 28.93-2.46 34.2-1.82 4.93-5.86 8.17-11.51 10.02A41.7 41.7 0 0 1 506 93.01c-5.79 0-9 2.4-12.2 7.64-.37.59-1.55 2.6-1.71 2.87-1.75 2.9-3.05 4.33-4.93 4.95-.94.32-2.07.83-3.87 1.74l-2.43 1.23c-1.03.53-1.87.94-2.7 1.34-6.43 3.1-11.73 4.72-17.16 4.72-5.71 0-10.04 2.09-14.02 5.92-1.16 1.11-4.2 4.53-4.63 4.94-2.54 2.44-5.93 4.24-10.85 6.1-1.4.52-5.98 2.13-6.25 2.22l-2.06.78c-.89.36-1.78.63-2.7.81-5.55 1.14-11.14-.54-17.98-4.42-1.27-.73-5.13-3.06-5.76-3.42-2.05-1.16-4.12-1.53-9.09-1.9l-1.73-.15c-4.78-.4-7.68-1.14-10.22-2.97-5-3.61-6.77-7.76-5.65-12.33 1.33-5.42 6.5-11.02 14.85-17.28a169.2 169.2 0 0 1 6.5-4.61c-.33.23 4.33-2.92 5.3-3.6 2.73-1.91 4.8-3.9 12.75-12.04l1.09-1.1c3.49-3.56 5.89-5.89 8.12-7.83 2.9-2.5 4.72-5.95 7.5-13.05l.63-1.61c2.7-6.92 4.28-10 6.87-12.33 1.42-1.28 6.68-6.54 7.93-7.5 3.98-3 8.01-2.73 19.57 1.4zm-.34.94c-11.26-4.02-15-4.28-18.62-1.53-1.19.9-6.4 6.11-7.88 7.43-2.42 2.18-3.96 5.19-6.6 11.95l-.63 1.61c-2.83 7.26-4.72 10.8-7.77 13.45a141.85 141.85 0 0 0-9.16 8.87c-8.02 8.2-10.08 10.2-12.88 12.16-.99.69-5.65 3.84-5.31 3.6-2.5 1.71-4.52 3.13-6.47 4.59-8.17 6.13-13.23 11.6-14.48 16.72-1.02 4.15.58 7.9 5.26 11.27 2.36 1.7 5.11 2.4 9.72 2.8l1.73.13c5.12.4 7.28.78 9.5 2.05.65.36 4.5 2.7 5.76 3.4 6.66 3.78 12.04 5.4 17.29 4.32.86-.17 1.7-.42 2.52-.75a67 67 0 0 1 2.1-.8c.28-.1 4.86-1.7 6.24-2.22 4.8-1.8 8.08-3.56 10.5-5.88.4-.38 3.44-3.8 4.63-4.94 4.16-4 8.72-6.2 14.72-6.2 5.25 0 10.42-1.59 16.73-4.62.82-.4 1.65-.8 2.68-1.33.12-.06 1.93-.99 2.43-1.23 1.84-.93 3-1.46 4-1.8 1.6-.52 2.76-1.82 4.39-4.52l1.7-2.88c3.39-5.5 6.87-8.11 13.07-8.11 4.45 0 8.73-.49 12.64-1.77 5.4-1.76 9.2-4.8 10.9-9.41 1.87-5.11 2.9-19.75 2.39-33.83-.56-15.53-2.81-26.48-6.08-27.52-3.18-1.02-6.57-.55-10.5 1.23-3.02 1.37-5.47 2.94-11 6.83-5.57 3.92-8.05 5.5-11.14 6.92-11.13 5.05-18.26 5.05-32.38.01zM475 55c5.38 0 7.55-.21 9.72-.96 1.26-.43 9.95-4.8 14.88-6.96 1.9-.82 3.56-2.44 6.6-6.04 2.56-3.04 3.19-3.75 4.4-4.84 3.7-3.35 7.07-3.28 10.22 1.23 6.23 8.9 5.61 15.94.07 27.02a71.26 71.26 0 0 0-2.5 5.48c-.32.8-1 2.7-1.09 2.9-.17.45-.34.81-.54 1.17-.63 1.14-1.56 2.21-4.05 4.7-2.4 2.4-5.16 3.27-11.68 4.33-1.81.3-2.2.36-3 .51-6.02 1.1-9.6 2.69-12.24 6.07-3.57 4.59-7.9 7.48-14.98 10.74-.55.24-1.1.5-1.8.8l-1.78.8a60.08 60.08 0 0 0-7.7 3.9c-2.57 1.6-4.79 2.35-9.42 3.46-8.58 2.06-12.28 3.76-17.37 9.36-5.12 5.64-10.17 7.64-16.63 6.7-5.36-.79-10.63-3.01-23.56-9.48-6.3-3.15-6.43-7.78-1.5-13.56 3.38-3.94 3.52-4.06 19.4-16.44 8.12-6.33 12.97-10.57 16.63-14.88 2.53-2.98 4.2-5.73 4.96-8.3 5.5-18.3 12.5-21.98 22.78-15.56 1.95 1.22 6.61 4.55 7.18 4.9 3.36 2.15 6.52 2.95 13 2.95zm0 2c-6.84 0-10.37-.89-14.08-3.26-.63-.4-5.27-3.71-7.16-4.9-9.05-5.65-14.66-2.7-19.8 14.45-.86 2.87-2.67 5.85-5.35 9.01-3.78 4.45-8.7 8.75-16.94 15.17-15.66 12.21-15.86 12.38-19.1 16.16-4.17 4.9-4.09 8 .88 10.48 12.71 6.35 17.89 8.54 22.94 9.28 5.78.84 10.18-.9 14.87-6.06 5.42-5.96 9.45-7.82 18.38-9.96 4.43-1.07 6.5-1.76 8.83-3.22a61.7 61.7 0 0 1 7.94-4.02l1.78-.8 1.78-.8c6.82-3.13 10.91-5.87 14.24-10.14 3-3.87 7-5.64 13.46-6.82.83-.15 1.21-.21 3.04-.51 6.1-1 8.6-1.78 10.58-3.77 2.36-2.36 3.21-3.34 3.72-4.26.15-.27.29-.56.44-.94.06-.15.75-2.06 1.09-2.9.64-1.6 1.45-3.4 2.57-5.64 5.24-10.49 5.8-16.8.07-24.98-2.4-3.44-4.37-3.48-7.24-.89-1.11 1-1.73 1.7-4.22 4.65-3.24 3.85-5.04 5.59-7.32 6.59-4.82 2.1-13.62 6.53-15.03 7.01-2.44.84-4.79 1.07-10.37 1.07zm-12.7 8.6c5.47 3.9 10.34 3.72 18.23.88 5.39-1.94 5.92-2.1 7.7-2.1 2.5-.01 4.21 1.36 5.24 4.46 1.66 4.98-2.32 8.52-12.3 12.68-2.7 1.13-16.25 6.18-20 7.73-7.86 3.24-13.93 6.42-18.87 10.15-13.02 9.84-18.36 11.93-23.71 9.68a24.67 24.67 0 0 1-3.62-1.98l-1.99-1.28a90.4 90.4 0 0 0-2.24-1.4c-3.33-2-2.82-4.28.85-7.34 1.35-1.13 10.66-7.61 13.53-9.91 7.1-5.69 11.91-11.47 14.41-18.34 3.07-8.45 4.89-12.1 6.8-13.39 1.73-1.16 3.36-.53 6.18 1.9.63.56 3.4 3.08 4.11 3.7 1.93 1.7 3.71 3.15 5.67 4.55zm-.6.8c-1.98-1.42-3.79-2.88-5.74-4.6-.73-.64-3.48-3.16-4.1-3.7-2.5-2.16-3.75-2.65-4.97-1.83-1.66 1.11-3.44 4.7-6.42 12.9-2.57 7.07-7.5 12.99-14.72 18.78-2.91 2.33-12.21 8.8-13.52 9.9-3.22 2.68-3.56 4.17-.97 5.72l2.26 1.4 1.99 1.28c1.47.93 2.48 1.5 3.47 1.91 4.9 2.07 9.96.07 22.72-9.56 5.02-3.79 11.15-7 19.1-10.28 3.76-1.55 17.3-6.6 20-7.72 9.5-3.97 13.14-7.2 11.73-11.44-.9-2.71-2.25-3.8-4.3-3.79-1.6 0-2.15.17-7.36 2.05-8.17 2.94-13.34 3.14-19.16-1.01z'%3E%3C/path%3E%3C/svg%3E"); diff --git a/resources/js/App/FAQs.vue b/resources/js/App/FAQs.vue index 5b2a2ab67..dc9785e8d 100644 --- a/resources/js/App/FAQs.vue +++ b/resources/js/App/FAQs.vue @@ -1,49 +1,74 @@ + + diff --git a/resources/js/Jetstream/ActionMessage.vue b/resources/js/Jetstream/ActionMessage.vue index c7b11d6fb..f2dd0b611 100644 --- a/resources/js/Jetstream/ActionMessage.vue +++ b/resources/js/Jetstream/ActionMessage.vue @@ -14,6 +14,11 @@ diff --git a/resources/js/Jetstream/AuthenticationCard.vue b/resources/js/Jetstream/AuthenticationCard.vue index 17f824798..6f9230999 100644 --- a/resources/js/Jetstream/AuthenticationCard.vue +++ b/resources/js/Jetstream/AuthenticationCard.vue @@ -1,15 +1,61 @@ + + diff --git a/resources/js/Jetstream/AuthenticationCardLogo.vue b/resources/js/Jetstream/AuthenticationCardLogo.vue index 5e113edd4..f05c024e8 100644 --- a/resources/js/Jetstream/AuthenticationCardLogo.vue +++ b/resources/js/Jetstream/AuthenticationCardLogo.vue @@ -1,15 +1,15 @@ diff --git a/resources/js/Jetstream/Button.vue b/resources/js/Jetstream/Button.vue index 38548aede..5e030d658 100644 --- a/resources/js/Jetstream/Button.vue +++ b/resources/js/Jetstream/Button.vue @@ -1,7 +1,7 @@ diff --git a/resources/js/Jetstream/ConfirmationModal.vue b/resources/js/Jetstream/ConfirmationModal.vue index 6891c6a62..5e525b19a 100644 --- a/resources/js/Jetstream/ConfirmationModal.vue +++ b/resources/js/Jetstream/ConfirmationModal.vue @@ -37,7 +37,9 @@ -
+
diff --git a/resources/js/Jetstream/Input.vue b/resources/js/Jetstream/Input.vue index 67d9030de..cb4b7a976 100644 --- a/resources/js/Jetstream/Input.vue +++ b/resources/js/Jetstream/Input.vue @@ -1,7 +1,7 @@
-
-
- - - + + + +
+
-
-
-
+
+
-

- You have no projects or samples yet +

+ Your workspace is empty

-
-

- Get started by uploading your data. -

- - +

+ Projects and compounds will appear here once you begin a + submission. Use Upload to add datasets and metadata to + nmrXiv. +

+
+ +

Need Help? - - + rel="noopener noreferrer" + class="font-medium text-indigo-600 underline decoration-indigo-600/30 underline-offset-2 transition-colors hover:text-indigo-800 hover:decoration-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300" + >Submission guide + — step-by-step help for preparing your data. +

-
+
    import AppLayout from "@/Layouts/AppLayout.vue"; import TeamProjects from "@/Pages/Project/Index.vue"; -import TeamSamples from "@/Shared/Samples.vue"; +import CompoundCards from "@/Shared/CompoundCards.vue"; import Create from "@/Shared/CreateButton.vue"; import Onboarding from "@/App/Onboarding.vue"; +import SearchInput from "@/Shared/SearchInput.vue"; +import EmptySearchState from "@/Shared/EmptySearchState.vue"; +import StatusFilter from "@/Shared/StatusFilter.vue"; +import Pagination from "@/Shared/Pagination.vue"; +import StudyCard from "@/Shared/StudyCard.vue"; +import { + ClockIcon, + StarIcon, + TrashIcon, + UserGroupIcon, +} from "@heroicons/vue/24/outline"; import { useMagicKeys } from "@vueuse/core"; import { getCurrentInstance } from "vue"; import { watchEffect } from "vue"; -import { Link } from "@inertiajs/vue3"; +import { Link, router } from "@inertiajs/vue3"; const { meta, u } = useMagicKeys(); @@ -373,12 +913,62 @@ export default { components: { AppLayout, TeamProjects, - TeamSamples, + CompoundCards, Create, Onboarding, + SearchInput, + EmptySearchState, + StatusFilter, + Pagination, + StudyCard, Link, + UserGroupIcon, + ClockIcon, + StarIcon, + TrashIcon, + }, + props: { + user: { + type: Object, + required: true, + }, + team: { + type: Object, + default: null, + }, + projects: { + type: Object, + required: true, + }, + samples: { + type: Object, + required: true, + }, + teamRole: { + type: [String, Object], + default: null, + }, + filters: { + type: Object, + required: true, + }, + hasProjects: { + type: Boolean, + default: false, + }, + hasSamples: { + type: Boolean, + default: false, + }, + workspaceProjects: { + type: Array, + default: () => [], + }, + workspaceStudies: { + type: Array, + default: () => [], + }, }, - props: ["user", "team", "projects", "samples", "teamRole", "filters"], setup() { const app = getCurrentInstance(); const openDatasetCreateDialog = (data) => { @@ -401,10 +991,13 @@ export default { }, data() { return { - selectedTab: "projects", + projectSearchTimer: null, + sampleSearchTimer: null, + perPageOptions: [5, 10, 25, 50], + /** Compound library tab: page size options (default 12). */ + samplesPerPageOptions: [12, 24, 36, 48, 50], }; }, - computed: { mailFromAddress() { return String(this.$page.props.mailFromAddress); @@ -413,6 +1006,114 @@ export default { mailTo() { return "mailto:" + String(this.$page.props.mailFromAddress); }, + + selectedTab() { + return this.filters.tab === "samples" ? "samples" : "projects"; + }, + + editableTeamRole() { + return ( + this.teamRole && + (this.teamRole == "owner" || this.teamRole == "admin") + ); + }, + + showDashboardLists() { + if (!this.team) { + return false; + } + return this.hasProjects || this.hasSamples || this.isWorkspaceView; + }, + + isWorkspaceView() { + const w = this.filters.workspace || "default"; + + return ["shared", "recent", "starred", "trashed"].includes(w); + }, + + workspaceSectionHeadingId() { + return "dashboard-workspace-heading"; + }, + + workspaceCopy() { + const w = this.filters.workspace || "default"; + /** @type {Record>} */ + const map = { + shared: { + title: "Shared with me", + description: + "Projects and compounds others have shared with you appear here once you accept an invitation.", + emptyProjectsTitle: "Nothing shared yet", + emptyProjectsBody: + "When a colleague invites you to a project or grants access, it will be listed here. Invitations are sent by email—accept them to see shared items in this workspace.", + emptyIcon: "UserGroupIcon", + }, + recent: { + title: "Recent", + description: + "Projects you have edited recently, across teams you belong to, sorted by latest activity.", + emptyProjectsTitle: "No recent projects", + emptyProjectsBody: + "Once you create or update a project, it will appear here for quicker access. Open the main workspace view to start or continue a submission.", + emptyIcon: "ClockIcon", + }, + starred: { + title: "Starred", + description: + "Pin projects and compounds you refer to often. Starred items are personal to your account.", + emptyProjectsTitle: "No starred items", + emptyProjectsBody: + "Use the star action on a project or compound card to add it here. Starred items stay easy to find across sessions.", + emptyIcon: "StarIcon", + }, + trashed: { + title: "Trash", + description: + "Projects you delete are retained here until they are permanently removed or restored.", + emptyProjectsTitle: "Trash is empty", + emptyProjectsBody: + "Deleted projects will appear in this list. You may restore a project from its menu while it remains in trash, subject to your workspace rules.", + emptyIcon: "TrashIcon", + }, + }; + + const entry = map[w] ?? map.recent; + const iconMap = { + UserGroupIcon, + ClockIcon, + StarIcon, + TrashIcon, + }; + + return { + ...entry, + emptyIcon: iconMap[entry.emptyIcon] ?? ClockIcon, + }; + }, + + showProjectsPerPageSelect() { + const total = Number(this.projects?.total) || 0; + const per = Number(this.filters.projects_per_page) || 10; + + return total >= per; + }, + + showSamplesPerPageSelect() { + const total = Number(this.samples?.total) || 0; + const per = Number(this.filters.samples_per_page) || 12; + + return total >= per; + }, + }, + + watch: { + "filters.action"(action) { + if (action === "submission") { + this.emitter.emit("openDatasetCreateDialog", { + draft_id: this.filters.draft_id, + }); + } + }, }, mounted() { @@ -421,10 +1122,171 @@ export default { draft_id: this.filters.draft_id, }); } + }, + + beforeUnmount() { + clearTimeout(this.projectSearchTimer); + clearTimeout(this.sampleSearchTimer); + }, + methods: { + /** + * Omit default / empty query keys so bookmarks and Inertia visits stay readable. + * + * @param {Record} merged + */ + compactDashboardParams(merged) { + const tab = merged.tab === "samples" ? "samples" : "projects"; + const projectsPage = Number(merged.projects_page) || 1; + const samplesPage = Number(merged.samples_page) || 1; + const projectsPerPage = Number(merged.projects_per_page) || 10; + const samplesPerPage = Number(merged.samples_per_page) || 12; + const projectsStatus = merged.projects_status || "all"; + const samplesStatus = merged.samples_status || "all"; + const projectsQ = String(merged.projects_q || "").trim(); + const samplesQ = String(merged.samples_q || "").trim(); + const workspace = merged.workspace || "default"; + + /** @type {Record} */ + const out = {}; + + if (workspace !== "default") { + out.workspace = workspace; + } + + if (tab !== "projects") { + out.tab = tab; + } + if (projectsPage !== 1) { + out.projects_page = projectsPage; + } + if (samplesPage !== 1) { + out.samples_page = samplesPage; + } + if (projectsPerPage !== 10) { + out.projects_per_page = projectsPerPage; + } + if (samplesPerPage !== 12) { + out.samples_per_page = samplesPerPage; + } + if (projectsStatus !== "all") { + out.projects_status = projectsStatus; + } + if (samplesStatus !== "all") { + out.samples_status = samplesStatus; + } + if (projectsQ !== "") { + out.projects_q = projectsQ; + } + if (samplesQ !== "") { + out.samples_q = samplesQ; + } + if (merged.action) { + out.action = merged.action; + } + if ( + merged.draft_id !== null && + merged.draft_id !== undefined && + merged.draft_id !== "" + ) { + out.draft_id = merged.draft_id; + } + + return out; + }, + + dashboardUrl(overrides = {}) { + const merged = { ...this.filters, ...overrides }; + + return this.route("dashboard", this.compactDashboardParams(merged)); + }, + + visitDashboard(overrides = {}) { + router.get( + this.dashboardUrl(overrides), + {}, + { + preserveState: true, + preserveScroll: true, + replace: true, + } + ); + }, + + onTabSelect(event) { + this.visitDashboard({ tab: event.target.value }); + }, - const urlSearchParams = new URLSearchParams(window.location.search); - const params = Object.fromEntries(urlSearchParams.entries()); - this.selectedTab = params["tab"] ? params["tab"] : "projects"; + onProjectsSearchInput(value) { + clearTimeout(this.projectSearchTimer); + this.projectSearchTimer = setTimeout(() => { + this.visitDashboard({ + projects_q: value || "", + projects_page: 1, + }); + }, 300); + }, + + onSamplesSearchInput(value) { + clearTimeout(this.sampleSearchTimer); + this.sampleSearchTimer = setTimeout(() => { + this.visitDashboard({ + samples_q: value || "", + samples_page: 1, + }); + }, 300); + }, + + onProjectsStatus(status) { + this.visitDashboard({ + projects_status: status, + projects_page: 1, + }); + }, + + onSamplesStatus(status) { + this.visitDashboard({ + samples_status: status, + samples_page: 1, + }); + }, + + onProjectsPerPageChange(event) { + const n = Number(event.target.value); + if (!Number.isFinite(n)) { + return; + } + this.visitDashboard({ + projects_per_page: n, + projects_page: 1, + }); + }, + + onSamplesPerPageChange(event) { + const n = Number(event.target.value); + if (!Number.isFinite(n)) { + return; + } + this.visitDashboard({ + samples_per_page: n, + samples_page: 1, + }); + }, + + clearProjectFilters() { + this.visitDashboard({ + projects_q: "", + projects_status: "all", + projects_page: 1, + }); + }, + + clearSampleFilters() { + this.visitDashboard({ + samples_q: "", + samples_status: "all", + samples_page: 1, + }); + }, }, }; diff --git a/resources/js/Pages/Predict.vue b/resources/js/Pages/Predict.vue new file mode 100644 index 000000000..f6ce5c9db --- /dev/null +++ b/resources/js/Pages/Predict.vue @@ -0,0 +1,565 @@ + + + diff --git a/resources/js/Pages/PrivacyPolicy.vue b/resources/js/Pages/PrivacyPolicy.vue index 8888d9277..321a716d5 100644 --- a/resources/js/Pages/PrivacyPolicy.vue +++ b/resources/js/Pages/PrivacyPolicy.vue @@ -1,30 +1,85 @@ diff --git a/resources/js/Pages/Project/Partials/Details.vue b/resources/js/Pages/Project/Partials/Details.vue index d73e7a2e3..8629577da 100644 --- a/resources/js/Pages/Project/Partials/Details.vue +++ b/resources/js/Pages/Project/Partials/Details.vue @@ -261,6 +261,7 @@ ref="photo" type="file" class="hidden" + accept="image/jpeg,image/png,image/gif,image/webp" @change=" updatePhotoPreview " @@ -978,6 +979,7 @@ export default defineComponent({ tags: [], doi: this.project.doi, tags_array: [], + project_tags_updated: true, photo: null, }), open: false, diff --git a/resources/js/Pages/Project/Settings.vue b/resources/js/Pages/Project/Settings.vue index 0fdd77fd3..3b652cd8a 100644 --- a/resources/js/Pages/Project/Settings.vue +++ b/resources/js/Pages/Project/Settings.vue @@ -10,7 +10,12 @@ > {{ project.name }} / Settings diff --git a/resources/js/Pages/Project/Show.vue b/resources/js/Pages/Project/Show.vue index ea34f223d..52241d5ec 100644 --- a/resources/js/Pages/Project/Show.vue +++ b/resources/js/Pages/Project/Show.vue @@ -1,3 +1,8 @@ +