feat: add FrankenPHP and Laravel Octane (#2219)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added optional Octane worker mode, updated Docker runtime, and a CI smoke-test workflow for container validation. * **Documentation** * Added production deployment and Octane guidance, plus notes for module authors about request-safe state. * **Bug Fixes** * Ensure activity logging resets per request, deterministic theme selection, and stable module link handling across requests. * **Tests** * Added Octane-focused tests for logging reset, theme isolation, and module link stability. * **Chores** * Removed obsolete local dev/example scripts, compose/make/just helpers, and related example configs. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/phpvms/phpvms/pull/2219?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
commit
e7c3cc017e
148
.dockerignore
Normal file
148
.dockerignore
Normal file
@ -0,0 +1,148 @@
|
||||
# .dockerignore
|
||||
# Generated by configs.sh
|
||||
|
||||
# Secrets & sensitive files (ALWAYS EXCLUDED FOR SECURITY)
|
||||
.env
|
||||
.env.*
|
||||
*.key
|
||||
*.pem
|
||||
*_rsa
|
||||
*_rsa.*
|
||||
*.p12
|
||||
*.pfx
|
||||
.aws/
|
||||
.ssh/
|
||||
credentials
|
||||
secrets.yml
|
||||
secrets.yaml
|
||||
|
||||
# Laravel/PHP
|
||||
.phpunit.cache
|
||||
bootstrap/cache/*
|
||||
!bootstrap/cache/.gitignore
|
||||
public/build
|
||||
storage/framework/cache/data/*
|
||||
storage/framework/sessions/*
|
||||
storage/framework/views/*
|
||||
storage/logs/*
|
||||
storage/*.key
|
||||
.phpstorm.meta.php
|
||||
.phpunit.result.cache
|
||||
.editorconfig
|
||||
.htaccess
|
||||
_ide_helper.php
|
||||
boost.json
|
||||
phpstan.neon
|
||||
phpunit.xml
|
||||
pint.json
|
||||
railpack.json
|
||||
rector.php
|
||||
version.json
|
||||
|
||||
# Node.js
|
||||
node_modules/
|
||||
.npm/
|
||||
.yarn/
|
||||
.bun/
|
||||
.pnp.*
|
||||
dist/
|
||||
build/
|
||||
.next/
|
||||
.nuxt/
|
||||
.cache/
|
||||
.turbo/
|
||||
out/
|
||||
.oxfmtrc.json
|
||||
.oxlintrc.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
|
||||
### Docker
|
||||
.docker
|
||||
Dockerfile
|
||||
compose.yaml
|
||||
compose.*
|
||||
|
||||
# Version control
|
||||
.git/
|
||||
.gitignore
|
||||
.gitattributes
|
||||
.gitmodules
|
||||
.svn/
|
||||
.hg/
|
||||
|
||||
# IDE & Editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
README*
|
||||
LICENSE*
|
||||
CHANGELOG*
|
||||
docs/
|
||||
documentation/
|
||||
.readthedocs.yml
|
||||
mkdocs.yml
|
||||
|
||||
# Misc
|
||||
.agents
|
||||
.lean-ctx
|
||||
.opencode
|
||||
.rtk
|
||||
.serena
|
||||
.superpowers
|
||||
docs/superpowers
|
||||
openspec
|
||||
CHANGELOG.md
|
||||
|
||||
# Tests & Coverage
|
||||
test/
|
||||
tests/
|
||||
__tests__/
|
||||
*.test.js
|
||||
*.test.ts
|
||||
*.spec.js
|
||||
*.spec.ts
|
||||
*.test.py
|
||||
*_test.go
|
||||
coverage/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.pytest_cache/
|
||||
.tox/
|
||||
jest.config.js
|
||||
vitest.config.js
|
||||
|
||||
# CI/CD
|
||||
.github/
|
||||
.gitlab-ci.yml
|
||||
.circleci/
|
||||
.travis.yml
|
||||
azure-pipelines.yml
|
||||
Jenkinsfile
|
||||
.drone.yml
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
logs/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# OS-specific files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
@ -49,6 +49,14 @@ APP_FALLBACK_LOCALE=en
|
||||
# Locale used by Faker when generating fake data.
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
# =============================================================================
|
||||
# OCTANE AND PRODUCTION CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# If you're using the docker-compose or Railpack to serve directly
|
||||
# to the internet, instead of a server in front of it, set this to true
|
||||
OCTANE_HTTPS=false
|
||||
|
||||
# =============================================================================
|
||||
# DATABASE
|
||||
# =============================================================================
|
||||
|
||||
120
.github/workflows/build.yml
vendored
120
.github/workflows/build.yml
vendored
@ -31,11 +31,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# https://github.com/marketplace/actions/nerdbank-gitversioning
|
||||
- name: Nerdbank.GitVersioning
|
||||
uses: dotnet/nbgv@v0.4.2
|
||||
with:
|
||||
setAllVars: true
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
|
||||
# Configure Caching
|
||||
- name: Setup cache environment
|
||||
@ -89,23 +85,16 @@ jobs:
|
||||
composer install --dev --no-interaction --no-scripts --verbose
|
||||
composer global require laravel/pint
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "npm"
|
||||
|
||||
- name: Install NPM dependencies
|
||||
run: |
|
||||
npm install
|
||||
npm run fmt config/version.yml
|
||||
# Need to lint version.yml since it’s updated in CI
|
||||
bun install
|
||||
bun run fmt config/version.yml
|
||||
|
||||
- name: Lint & Format Check
|
||||
run: npm run lint && npm run fmt:check
|
||||
run: bun run lint && bun run fmt:check
|
||||
|
||||
- name: Compile assets
|
||||
run: npm run build
|
||||
run: bun run build
|
||||
|
||||
- name: Run Composer post-autoload scripts
|
||||
run: |
|
||||
@ -125,10 +114,6 @@ jobs:
|
||||
- name: Run PHPStan
|
||||
run: vendor/bin/phpstan analyse --memory-limit=2G
|
||||
|
||||
# - name: Run Rector
|
||||
# run: |
|
||||
# vendor/bin/rector --dry-run
|
||||
|
||||
# This runs after all of the tests, run have run. Creates a cleaned up version of the
|
||||
# distro, and then creates the artifact to push up to S3 or wherever
|
||||
artifacts:
|
||||
@ -147,11 +132,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# https://github.com/marketplace/actions/nerdbank-gitversioning
|
||||
- name: Nerdbank.GitVersioning
|
||||
uses: dotnet/nbgv@v0.4.2
|
||||
with:
|
||||
setAllVars: true
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
@ -183,17 +164,11 @@ jobs:
|
||||
composer install --no-dev --prefer-dist --no-interaction --no-scripts --verbose
|
||||
sudo chmod +x ./.github/scripts/*
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "npm"
|
||||
|
||||
- name: Install NPM dependencies
|
||||
run: npm install
|
||||
run: bun install
|
||||
|
||||
- name: Compile assets
|
||||
run: npm run build
|
||||
run: bun run build
|
||||
|
||||
- name: Run Composer post-autoload scripts
|
||||
run: composer dump-autoload
|
||||
@ -245,53 +220,6 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Configure Caching
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
# Three-step bootstrap (see `build` job comment for full rationale):
|
||||
# 1. composer install --no-scripts: vendor/ for Vite CSS @imports
|
||||
# 2. npm build: writes public/build manifest + dist files
|
||||
# 3. composer dump-autoload: re-fires post-autoload-dump cleanly
|
||||
- name: "Install Release Dependencies (no scripts)"
|
||||
run: |
|
||||
rm -rf vendor
|
||||
composer install --no-dev --prefer-dist --no-interaction --no-scripts --verbose
|
||||
sudo chmod +x ./.github/scripts/*
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "npm"
|
||||
|
||||
- name: Install NPM dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Compile assets
|
||||
run: npm run build
|
||||
|
||||
- name: Run Composer post-autoload scripts
|
||||
run: composer dump-autoload
|
||||
|
||||
# https://github.com/marketplace/actions/nerdbank-gitversioning
|
||||
- name: Nerdbank.GitVersioning
|
||||
uses: dotnet/nbgv@v0.4.2
|
||||
with:
|
||||
setAllVars: true
|
||||
|
||||
- id: version
|
||||
name: Get version
|
||||
run: .github/scripts/version.sh
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@ -299,28 +227,16 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
- name: Build and push Docker images
|
||||
uses: iloveitaly/github-action-railpack@master
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
push: false
|
||||
tags: |
|
||||
type=semver,pattern={{version}},value=${{ env.NBGV_SemVer2 }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=${{ env.NBGV_SemVer2 }}
|
||||
type=ref,event=branch
|
||||
${{ env.IMAGE_NAME }}:latest
|
||||
|
||||
- name: Build and push
|
||||
id: push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
# - name: Generate artifact attestation
|
||||
# uses: actions/attest-build-provenance@v2
|
||||
# with:
|
||||
# subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
# subject-digest: ${{ steps.push.outputs.digest }}
|
||||
# push-to-registry: true
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -107,3 +107,4 @@ modules_statuses.json
|
||||
# Local SQLite scratch db (test artifact)
|
||||
/phpvms
|
||||
.env.testing
|
||||
/intellij_style.xml
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<Version>3.7.115</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
63
Dockerfile
63
Dockerfile
@ -1,63 +0,0 @@
|
||||
# This Dockerfile is used to create the base phpVMS image for Docker in production.
|
||||
# It is based on https://serversideup.net/open-source/docker-php/.
|
||||
FROM composer:latest AS vendor
|
||||
|
||||
LABEL org.opencontainers.image.description="The official phpVMS image"
|
||||
|
||||
COPY composer.json composer.json
|
||||
COPY composer.lock composer.lock
|
||||
|
||||
COPY database database
|
||||
|
||||
RUN composer install \
|
||||
--ignore-platform-reqs \
|
||||
--no-interaction \
|
||||
--no-plugins \
|
||||
--no-scripts \
|
||||
--prefer-dist
|
||||
|
||||
FROM serversideup/php:8.4-fpm
|
||||
|
||||
ARG WWWUSER=1000
|
||||
ARG WWWGROUP=1000
|
||||
|
||||
# Switch to root so we can do root things
|
||||
USER root
|
||||
|
||||
# Install missing extensions with root permissions
|
||||
RUN install-php-extensions intl bcmath
|
||||
|
||||
# Install mariadb client (required for backups)
|
||||
RUN apt-get update; \
|
||||
apt-get upgrade -yqq; \
|
||||
apt-get install -yqq --no-install-recommends --show-progress \
|
||||
mariadb-client
|
||||
|
||||
# Install nodejs
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Deal with permissions
|
||||
RUN usermod -ou $WWWUSER www-data \
|
||||
&& groupmod -og $WWWGROUP www-data
|
||||
|
||||
# Drop back to our unprivileged user
|
||||
USER www-data
|
||||
|
||||
# Copy application files
|
||||
COPY --chown=www-data:www-data . /var/www/html
|
||||
|
||||
# Copy deps from the composer build stage
|
||||
COPY --chown=www-data:www-data --from=vendor /app/vendor/ /var/www/html/vendor/
|
||||
|
||||
# Build assets
|
||||
# Note: the assets are moved to the build directory in the image, they'll be moved back to
|
||||
# the public directory in the entrypoint script. This allows to overwrite the volume
|
||||
# so that the assets are also updated in caddy.
|
||||
|
||||
RUN npm install && npm run build && \
|
||||
cp -R ./public/build/ ./build
|
||||
|
||||
COPY --chmod=755 ./resources/docker/run-dump-autoload.sh /etc/entrypoint.d/20-run-dump-autoload.sh
|
||||
COPY --chmod=755 ./resources/docker/copy-assets.sh /etc/entrypoint.d/30-copy-assets.sh
|
||||
68
Dockerfile.prod
Normal file
68
Dockerfile.prod
Normal file
@ -0,0 +1,68 @@
|
||||
# This Dockerfile is used to create the base phpVMS image for Docker in production.
|
||||
# It is based on https://serversideup.net/open-source/docker-php/.
|
||||
#
|
||||
# I highly recommend using the Railpack instead!
|
||||
FROM serversideup/php:8.5-cli AS build
|
||||
|
||||
LABEL org.opencontainers.image.description="The official phpvms image"
|
||||
|
||||
USER root
|
||||
|
||||
COPY --from=oven/bun:latest /usr/local/bin/bun /usr/local/bin/bun
|
||||
|
||||
RUN install-php-extensions intl bcmath
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN composer install \
|
||||
--no-interaction \
|
||||
--prefer-dist \
|
||||
--no-dev \
|
||||
--optimize-autoloader
|
||||
|
||||
RUN bun install && bun run build
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
#
|
||||
# Final image
|
||||
#
|
||||
|
||||
FROM serversideup/php:8.5-frankenphp
|
||||
|
||||
ARG WWWUSER=1000
|
||||
ARG WWWGROUP=1000
|
||||
|
||||
# Switch to root so we can do root things
|
||||
USER root
|
||||
|
||||
# Install missing extensions with root permissions
|
||||
RUN install-php-extensions intl bcmath
|
||||
|
||||
# Install mariadb client (required for backups)
|
||||
RUN apt-get update; \
|
||||
apt-get upgrade -yqq; \
|
||||
apt-get install -yqq --no-install-recommends --show-progress \
|
||||
mariadb-client
|
||||
|
||||
COPY --from=oven/bun:latest /usr/local/bin/bun /usr/local/bin/bun
|
||||
|
||||
# Deal with permissions
|
||||
RUN usermod -ou $WWWUSER www-data \
|
||||
&& groupmod -og $WWWGROUP www-data
|
||||
|
||||
# Drop back to our unprivileged user
|
||||
USER www-data
|
||||
|
||||
# Copy application files
|
||||
COPY --chown=www-data:www-data --from=build /build/ /var/www/html/
|
||||
COPY --chmod=755 ./resources/docker/run-dump-autoload.sh /etc/entrypoint.d/20-run-dump-autoload.sh
|
||||
|
||||
# The image keeps Serversideup's default FrankenPHP entrypoint (classic mode:
|
||||
# one PHP worker per request, identical request semantics to the prior FPM
|
||||
# setup). Production deployments override the container `command:` to launch
|
||||
# Laravel Octane worker mode — see `compose.deploy.yml` for the canonical
|
||||
# invocation. Octane is not started by this Dockerfile so that anyone
|
||||
# pulling the bare image (k8s, custom Compose, Swarm) gets a predictable,
|
||||
# documented runtime without surprise behaviour.
|
||||
98
Makefile
98
Makefile
@ -1,98 +0,0 @@
|
||||
#
|
||||
#
|
||||
# Create the phpvms database if needed:
|
||||
# docker exec phpvms /usr/bin/mysql -uroot -e 'CREATE DATABASE phpvms'
|
||||
SHELL := /bin/bash
|
||||
COMPOSER ?= $(shell which composer)
|
||||
|
||||
PKG_NAME := "/tmp"
|
||||
CURR_PATH=$(shell pwd)
|
||||
|
||||
.PHONY: all
|
||||
all: install
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@php artisan optimize:clear
|
||||
@find bootstrap/cache -type f -not -name '.gitignore' -print0 | xargs -0 rm -rf
|
||||
|
||||
@find storage/framework/cache/ -mindepth 1 -type f -not -name '.gitignore' -print0 | xargs -0 rm -rf
|
||||
@find storage/framework/sessions/ -mindepth 1 -type f -not -name '.gitignore' -print0 | xargs -0 rm -rf
|
||||
@find storage/framework/views/ -mindepth 1 -not -name '.gitignore' -print0 | xargs -0 rm -rf
|
||||
|
||||
@find storage/logs -mindepth 1 -not -name '.gitignore' -print0 | xargs -0 rm -rf
|
||||
|
||||
.PHONY: clean-routes
|
||||
clean-routes:
|
||||
@php artisan route:clear
|
||||
|
||||
.PHONY: clear
|
||||
clear:
|
||||
@php artisan optimize:clear
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
@php $(COMPOSER) install --no-interaction
|
||||
|
||||
# This is to build all the stylesheets, etc
|
||||
.PHONY: build-assets
|
||||
build-assets:
|
||||
npm run build
|
||||
|
||||
.PHONY: install
|
||||
install: build
|
||||
@php artisan db:create
|
||||
@php artisan migrate --seed
|
||||
@echo "Done!"
|
||||
|
||||
.PHONY: update
|
||||
update: build
|
||||
@php $(COMPOSER) dump-autoload
|
||||
@php $(COMPOSER) update --no-interaction
|
||||
@php artisan migrate --force
|
||||
@echo "Done!"
|
||||
|
||||
.PHONY: reset
|
||||
reset: clean
|
||||
@php $(COMPOSER) dump-autoload
|
||||
@make reload-db
|
||||
|
||||
.PHONY: reload-db
|
||||
reload-db:
|
||||
@php artisan db:create --reset
|
||||
@php artisan migrate --seed
|
||||
@echo "Done!"
|
||||
@make clean
|
||||
|
||||
.PHONY: tests
|
||||
tests: test
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@#php artisan db:create --reset
|
||||
@php artisan test -p
|
||||
|
||||
.PHONY: pint
|
||||
pint:
|
||||
@vendor/bin/pint --parallel
|
||||
|
||||
.PHONY: reset-installer
|
||||
reset-installer:
|
||||
@php artisan db:create --reset
|
||||
@php artisan migrate:refresh --seed
|
||||
|
||||
.PHONY: docker-test
|
||||
docker-test:
|
||||
@docker run --rm \
|
||||
-u $(shell id -u):$(shell id -g) \
|
||||
-v $(shell pwd):/var/www/html \
|
||||
-w /var/www/html \
|
||||
laravelsail/php84-composer:latest \
|
||||
composer install --ignore-platform-reqs
|
||||
@vendor/bin/sail up
|
||||
|
||||
.PHONY: docker-clean
|
||||
docker-clean:
|
||||
-docker stop phpvms
|
||||
-docker rm -rf phpvms
|
||||
-rm -rf tmp/mysql
|
||||
5
Procfile
5
Procfile
@ -1,5 +0,0 @@
|
||||
#dnsmasq: /usr/local/sbin/dnsmasq --keep-in-foreground
|
||||
php-fpm: /usr/local/sbin/php-fpm --nodaemonize
|
||||
nginx: /usr/local/bin/nginx -g 'daemon off;'
|
||||
mysql: docker-compose --file ~/docker/mysql/docker-compose.yml up
|
||||
#mailhog: /usr/local/bin/mailhog
|
||||
42
README.md
42
README.md
@ -36,6 +36,48 @@ A full distribution, with all the composer dependencies, is available at this
|
||||
|
||||
[View installation details](https://docs.phpvms.net/installation)
|
||||
|
||||
## Production Deployment with Docker
|
||||
|
||||
The reference production stack is `compose.deploy.yml`. It runs the official
|
||||
phpVMS image (built from the repo `Dockerfile`, based on
|
||||
`serversideup/php:8.5-frankenphp`) with Laravel Octane worker mode enabled,
|
||||
plus MariaDB and Redis. No separate web-server sidecar is needed — FrankenPHP
|
||||
serves HTTP/HTTPS directly.
|
||||
|
||||
```bash
|
||||
docker compose -f compose.deploy.yml up -d
|
||||
```
|
||||
|
||||
### Octane worker mode
|
||||
|
||||
The image itself ships with serversideup's default classic FrankenPHP entry
|
||||
point (one PHP worker per request). `compose.deploy.yml` opts into Laravel
|
||||
Octane worker mode via a `command:` override on the `app` service — the
|
||||
[pattern documented upstream](https://serversideup.net/open-source/docker-php/docs/framework-guides/laravel/octane).
|
||||
|
||||
To fall back to classic FrankenPHP + PHP-worker mode (matches the prior
|
||||
PHP-FPM request semantics — slower per request but bulletproof if a
|
||||
worker-mode bug hits), delete the `command:` line from the `app` service
|
||||
and recreate the container:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
# command: [...octane:start...] # remove this line
|
||||
```
|
||||
|
||||
Under worker mode, `KvpService` (the JSON-backed key/value store used for
|
||||
non-hot settings) is eventually consistent: writes from one worker
|
||||
propagate to other workers on their next read of the same key. If your
|
||||
deployment requires strong consistency for KVP, run with `--workers=1` or
|
||||
remove the `command:` override.
|
||||
|
||||
Module authors: under Octane the framework stays booted across requests,
|
||||
so avoid per-request data in singleton-bound services, `static` array
|
||||
accumulators, and `boot()`-time toggles without a matching per-request
|
||||
reset. See `app/Http/Middleware/DisableActivityLoggingByDefault.php` for
|
||||
the pattern used to keep activity logging request-scoped.
|
||||
|
||||
## Development Environment with Docker
|
||||
|
||||
A full development environment can be brought up using Docker and
|
||||
|
||||
35
app/Http/Middleware/DisableActivityLoggingByDefault.php
Normal file
35
app/Http/Middleware/DisableActivityLoggingByDefault.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Resets activity logging to "disabled" at the start of every request.
|
||||
*
|
||||
* Octane keeps the framework booted across requests, so a boot-time
|
||||
* activity()->disableLogging() runs only once per worker. Routes / panels
|
||||
* that flip logging on (EnableActivityLogging middleware, Filament's
|
||||
* admin panel bootUsing) would then leave logging on for every subsequent
|
||||
* request the worker handles. This middleware re-applies the default at
|
||||
* the start of each request so the EnableActivityLogging middleware and
|
||||
* Filament's per-panel toggle remain the explicit opt-ins.
|
||||
*
|
||||
* @phpstan-type Handler Closure(Request): Response
|
||||
*/
|
||||
class DisableActivityLoggingByDefault
|
||||
{
|
||||
/**
|
||||
* @param Handler $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
activity()->disableLogging();
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@ -41,14 +41,16 @@ class SetActiveTheme implements Middleware
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the theme for the current middleware
|
||||
* Set the theme for the current middleware.
|
||||
*
|
||||
* Octane keeps the application booted across requests, so the underlying
|
||||
* igaster/laravel-theme singleton retains whatever theme the previous
|
||||
* request set. Skipped paths (admin/api/install) must therefore reset
|
||||
* to the configured default explicitly — early-returning would let a
|
||||
* prior frontend request's theme leak into the next admin/api response.
|
||||
*/
|
||||
public function setTheme(Request $request): void
|
||||
{
|
||||
if ($request->is(self::$skip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$theme = setting('general.theme', 'seven');
|
||||
} catch (Exception $exception) {
|
||||
@ -56,8 +58,18 @@ class SetActiveTheme implements Middleware
|
||||
$theme = 'seven';
|
||||
}
|
||||
|
||||
if (!empty($theme)) {
|
||||
if (empty($theme)) {
|
||||
$theme = config('themes.default');
|
||||
}
|
||||
|
||||
if ($request->is(self::$skip)) {
|
||||
// Skipped paths don't pick a per-request theme but still need a
|
||||
// deterministic baseline under Octane (see method PHPDoc).
|
||||
Theme::set(config('themes.default'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Theme::set($theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,13 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
Paginator::useBootstrap();
|
||||
|
||||
activity()->disableLogging();
|
||||
// Activity logging defaults to disabled for every request. The reset
|
||||
// lives in App\Http\Middleware\DisableActivityLoggingByDefault (wired
|
||||
// in bootstrap/app.php) instead of here, because Octane runs boot()
|
||||
// only once per worker — the per-request reset has to live in the
|
||||
// request pipeline. The middleware reapplies the default before any
|
||||
// opt-in (EnableActivityLogging middleware, Filament panel boot)
|
||||
// gets a chance to flip it on.
|
||||
|
||||
/**
|
||||
* Inject the extra display + monospace fonts used by the docs design
|
||||
|
||||
@ -10,6 +10,13 @@ class KvpService
|
||||
{
|
||||
private readonly Valuestore $valueStore;
|
||||
|
||||
// TODO(octane): re-resolve per request or move to Redis-backed KV.
|
||||
// Spatie\Valuestore is a JSON-file wrapper that caches parsed contents
|
||||
// in-memory after writes. Under Octane the same instance lives for the
|
||||
// worker's lifetime, so writes from worker A propagate to worker B only
|
||||
// on B's next read of the same key — KVP is eventually consistent under
|
||||
// worker mode. See README.md > "Octane worker mode" for the operator-
|
||||
// facing note.
|
||||
public function __construct()
|
||||
{
|
||||
$this->valueStore = Valuestore::make(config('phpvms.kvp_storage_path'));
|
||||
|
||||
@ -22,12 +22,16 @@ use PharData;
|
||||
|
||||
class ModuleService extends Service
|
||||
{
|
||||
protected static array $adminLinks = [];
|
||||
/**
|
||||
* Module-registered admin nav links. Populated once per worker via each
|
||||
* module's boot()->registerLinks() call; not a per-request accumulator.
|
||||
*/
|
||||
protected array $adminLinks = [];
|
||||
|
||||
/**
|
||||
* @var array 0 == logged out, 1 == logged in
|
||||
*/
|
||||
protected static array $frontendLinks = [
|
||||
protected array $frontendLinks = [
|
||||
0 => [],
|
||||
1 => [],
|
||||
];
|
||||
@ -37,7 +41,7 @@ class ModuleService extends Service
|
||||
*/
|
||||
public function addFrontendLink(string $title, string $url, string $icon = 'bi bi-people', bool $logged_in = true): void
|
||||
{
|
||||
self::$frontendLinks[$logged_in][] = [
|
||||
$this->frontendLinks[$logged_in][] = [
|
||||
'title' => $title,
|
||||
'url' => $url,
|
||||
'icon' => $icon,
|
||||
@ -49,7 +53,7 @@ class ModuleService extends Service
|
||||
*/
|
||||
public function getFrontendLinks(mixed $logged_in): array
|
||||
{
|
||||
return self::$frontendLinks[$logged_in];
|
||||
return $this->frontendLinks[$logged_in];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,7 +61,7 @@ class ModuleService extends Service
|
||||
*/
|
||||
public function addAdminLink(string $title, string $url, string $icon = 'bi bi-people'): void
|
||||
{
|
||||
self::$adminLinks[] = [
|
||||
$this->adminLinks[] = [
|
||||
'title' => $title,
|
||||
'url' => $url,
|
||||
'icon' => $icon,
|
||||
@ -69,7 +73,7 @@ class ModuleService extends Service
|
||||
*/
|
||||
public function getAdminLinks(): array
|
||||
{
|
||||
return self::$adminLinks;
|
||||
return $this->adminLinks;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -7,6 +7,7 @@ use App\Exceptions\Converters\SymfonyException;
|
||||
use App\Exceptions\Converters\ValidationException;
|
||||
use App\Exceptions\Unauthenticated;
|
||||
use App\Http\Middleware\ApiAuth;
|
||||
use App\Http\Middleware\DisableActivityLoggingByDefault;
|
||||
use App\Http\Middleware\InstalledCheck;
|
||||
use App\Http\Middleware\SetActiveLanguage;
|
||||
use App\Http\Middleware\SetActiveTheme;
|
||||
@ -33,6 +34,14 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
// Prepend the activity-logging reset to both groups so every request
|
||||
// starts with logging disabled. EnableActivityLogging (and Filament's
|
||||
// panel boot hook) then opt in for the request that wants it. Required
|
||||
// for Octane safety — see config/octane.php and the change at
|
||||
// openspec/changes/switch-to-frankenphp-image/design.md.
|
||||
$middleware->prependToGroup('web', DisableActivityLoggingByDefault::class);
|
||||
$middleware->prependToGroup('api', DisableActivityLoggingByDefault::class);
|
||||
|
||||
$middleware->appendToGroup('web', [
|
||||
InstalledCheck::class,
|
||||
SetActiveTheme::class,
|
||||
|
||||
218
bun.lock
218
bun.lock
@ -4,7 +4,9 @@
|
||||
"workspaces": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"axios": "^1.15.0",
|
||||
"@date-fns/tz": "^1.5.0",
|
||||
"@preact/signals": "^2.9.0",
|
||||
"axios": "^1.15",
|
||||
"bootstrap": "~4.6",
|
||||
"chart.js": "^4.5.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
@ -18,22 +20,72 @@
|
||||
"leaflet.geodesic": "^2.5.2",
|
||||
"moment": "^2.29.1",
|
||||
"popper.js": "^1.15.0",
|
||||
"preact": "^10.29.2",
|
||||
"rivets": "^0.9.6",
|
||||
"select2": "^4.0.13",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.10.5",
|
||||
"@tailwindcss/vite": "^4.2.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"esbuild": "^0.28.0",
|
||||
"happy-dom": "^20.9.0",
|
||||
"laravel-vite-plugin": "^3.0.0",
|
||||
"oxfmt": "^0.41.0",
|
||||
"oxlint": "^1.56.0",
|
||||
"sass": "^1.54.5",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.0.10",
|
||||
"vitest": "^4.1.7",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="],
|
||||
|
||||
"@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="],
|
||||
|
||||
"@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="],
|
||||
|
||||
"@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="],
|
||||
|
||||
"@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="],
|
||||
|
||||
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="],
|
||||
|
||||
"@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="],
|
||||
|
||||
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="],
|
||||
|
||||
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="],
|
||||
|
||||
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="],
|
||||
|
||||
"@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="],
|
||||
|
||||
"@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-module-imports": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-syntax-jsx": "^7.29.7", "@babel/types": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.29.7", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g=="],
|
||||
|
||||
"@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="],
|
||||
|
||||
"@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="],
|
||||
|
||||
"@date-fns/tz": ["@date-fns/tz@1.5.0", "", {}, "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg=="],
|
||||
|
||||
"@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
|
||||
@ -212,6 +264,20 @@
|
||||
|
||||
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
|
||||
|
||||
"@preact/preset-vite": ["@preact/preset-vite@2.10.5", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@prefresh/vite": "^2.4.11", "@rollup/pluginutils": "^5.0.0", "babel-plugin-transform-hook-names": "^1.0.2", "debug": "^4.4.3", "magic-string": "^0.30.21", "picocolors": "^1.1.1", "vite-prerender-plugin": "^0.5.8", "zimmerframe": "^1.1.4" }, "peerDependencies": { "@babel/core": "7.x", "vite": "2.x || 3.x || 4.x || 5.x || 6.x || 7.x || 8.x" } }, "sha512-p0vJpxiVO7KWWazWny3LUZ+saXyZKWv6Ju0bYMWNJRp2YveufRPgSUB1C4MTqGJfz07EehMgfN+AJNwQy+w6Iw=="],
|
||||
|
||||
"@preact/signals": ["@preact/signals@2.9.1", "", { "dependencies": { "@preact/signals-core": "^1.14.0" }, "peerDependencies": { "preact": ">= 10.25.0 || >=11.0.0-0" } }, "sha512-xVqN8mJjbSN5IB/8Ubmd9NN+Ew6zJswoRxrjZbH3YsgkMshFeO6d8zxEFpHRTq9GJZx7cnPs2CnCpFqtGXGNsw=="],
|
||||
|
||||
"@preact/signals-core": ["@preact/signals-core@1.14.2", "", {}, "sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A=="],
|
||||
|
||||
"@prefresh/babel-plugin": ["@prefresh/babel-plugin@0.5.3", "", {}, "sha512-57LX2SHs4BX2s1IwCjNzTE2OJeEepRCNf1VTEpbNcUyHfMO68eeOWGDIt4ob9aYlW6PEWZ1SuwNikuoIXANDtQ=="],
|
||||
|
||||
"@prefresh/core": ["@prefresh/core@1.5.10", "", { "peerDependencies": { "preact": "^10.0.0 || ^11.0.0-0" } }, "sha512-7yPTFbG56sutaFu8krp3B4a200KOFUvrtlllKWRuLjsYXo9UUucHOZRcer+gtgMkFTpv6ob8TGcTwA32bSwa1w=="],
|
||||
|
||||
"@prefresh/utils": ["@prefresh/utils@1.2.1", "", {}, "sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw=="],
|
||||
|
||||
"@prefresh/vite": ["@prefresh/vite@2.4.12", "", { "dependencies": { "@babel/core": "^7.22.1", "@prefresh/babel-plugin": "^0.5.2", "@prefresh/core": "^1.5.0", "@prefresh/utils": "^1.2.0", "@rollup/pluginutils": "^4.2.1" }, "peerDependencies": { "preact": "^10.4.0 || ^11.0.0-0", "vite": ">=2.0.0" } }, "sha512-FY1fzXpUjiuosznMV0YM7XAOPZjB5FIdWS0W24+XnlxYkt9hNAwwsiKYn+cuTEoMtD/ZVazS5QVssBr9YhpCQA=="],
|
||||
|
||||
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.17", "", { "os": "android", "cpu": "arm64" }, "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ=="],
|
||||
|
||||
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw=="],
|
||||
@ -244,6 +310,10 @@
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.17", "", {}, "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg=="],
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
|
||||
@ -276,14 +346,54 @@
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||
|
||||
"@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
|
||||
|
||||
"@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
|
||||
|
||||
"@types/node": ["@types/node@25.9.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
|
||||
|
||||
"@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@vitest/expect": ["@vitest/expect@4.1.7", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w=="],
|
||||
|
||||
"@vitest/mocker": ["@vitest/mocker@4.1.7", "", { "dependencies": { "@vitest/spy": "4.1.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA=="],
|
||||
|
||||
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.7", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw=="],
|
||||
|
||||
"@vitest/runner": ["@vitest/runner@4.1.7", "", { "dependencies": { "@vitest/utils": "4.1.7", "pathe": "^2.0.3" } }, "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw=="],
|
||||
|
||||
"@vitest/snapshot": ["@vitest/snapshot@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "@vitest/utils": "4.1.7", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw=="],
|
||||
|
||||
"@vitest/spy": ["@vitest/spy@4.1.7", "", {}, "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q=="],
|
||||
|
||||
"@vitest/utils": ["@vitest/utils@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw=="],
|
||||
|
||||
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"axios": ["axios@1.15.0", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q=="],
|
||||
|
||||
"babel-plugin-transform-hook-names": ["babel-plugin-transform-hook-names@1.0.2", "", { "peerDependencies": { "@babel/core": "^7.12.10" } }, "sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"bootstrap": ["bootstrap@4.6.2", "", { "peerDependencies": { "jquery": "1.9.1 - 3", "popper.js": "^1.16.1" } }, "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ=="],
|
||||
|
||||
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "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" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001793", "", {}, "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA=="],
|
||||
|
||||
"chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
|
||||
|
||||
"chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="],
|
||||
|
||||
"chartjs-adapter-date-fns": ["chartjs-adapter-date-fns@3.0.0", "", { "peerDependencies": { "chart.js": ">=2.8.0", "date-fns": ">=2.0.0" } }, "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg=="],
|
||||
@ -294,28 +404,56 @@
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"cookieconsent": ["cookieconsent@3.1.1", "", {}, "sha512-v8JWLJcI7Zs9NWrs8hiVldVtm3EBF70TJI231vxn6YToBGj0c9dvdnYwltydkAnrbBMOM/qX1xLFrnTfm5wTag=="],
|
||||
|
||||
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
||||
|
||||
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
|
||||
"date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.363", "", {}, "sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
|
||||
|
||||
"entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||
|
||||
"esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": "bin/esbuild" }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
|
||||
@ -326,6 +464,8 @@
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
@ -334,12 +474,16 @@
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"happy-dom": ["happy-dom@20.9.0", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||
|
||||
"immutable": ["immutable@5.1.5", "", {}, "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
@ -350,6 +494,14 @@
|
||||
|
||||
"jquery": ["jquery@3.7.1", "", {}, "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="],
|
||||
|
||||
"laravel-vite-plugin": ["laravel-vite-plugin@3.0.0", "", { "dependencies": { "picocolors": "^1.0.0", "tinyglobby": "^0.2.12", "vite-plugin-full-reload": "^1.1.0" }, "peerDependencies": { "vite": "^8.0.0" }, "bin": { "clean-orphaned-assets": "bin/clean.js" } }, "sha512-eO6B/IPH448XNjyA5OmJsFAeJH3MeQGWodNMzejx44+rhV44cs0op05t4yku9ifaRnikDkSHLOLXszJL0tiW3w=="],
|
||||
|
||||
"leaflet": ["leaflet@1.9.4", "", {}, "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="],
|
||||
@ -384,6 +536,8 @@
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
@ -394,14 +548,26 @@
|
||||
|
||||
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
||||
|
||||
"node-html-parser": ["node-html-parser@6.1.13", "", { "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" } }, "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.46", "", {}, "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
|
||||
|
||||
"oxfmt": ["oxfmt@0.41.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.41.0", "@oxfmt/binding-android-arm64": "0.41.0", "@oxfmt/binding-darwin-arm64": "0.41.0", "@oxfmt/binding-darwin-x64": "0.41.0", "@oxfmt/binding-freebsd-x64": "0.41.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.41.0", "@oxfmt/binding-linux-arm-musleabihf": "0.41.0", "@oxfmt/binding-linux-arm64-gnu": "0.41.0", "@oxfmt/binding-linux-arm64-musl": "0.41.0", "@oxfmt/binding-linux-ppc64-gnu": "0.41.0", "@oxfmt/binding-linux-riscv64-gnu": "0.41.0", "@oxfmt/binding-linux-riscv64-musl": "0.41.0", "@oxfmt/binding-linux-s390x-gnu": "0.41.0", "@oxfmt/binding-linux-x64-gnu": "0.41.0", "@oxfmt/binding-linux-x64-musl": "0.41.0", "@oxfmt/binding-openharmony-arm64": "0.41.0", "@oxfmt/binding-win32-arm64-msvc": "0.41.0", "@oxfmt/binding-win32-ia32-msvc": "0.41.0", "@oxfmt/binding-win32-x64-msvc": "0.41.0" }, "bin": "bin/oxfmt" }, "sha512-sKLdJZdQ3bw6x9qKiT7+eID4MNEXlDHf5ZacfIircrq6Qwjk0L6t2/JQlZZrVHTXJawK3KaMuBoJnEJPcqCEdg=="],
|
||||
|
||||
"oxlint": ["oxlint@1.56.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.56.0", "@oxlint/binding-android-arm64": "1.56.0", "@oxlint/binding-darwin-arm64": "1.56.0", "@oxlint/binding-darwin-x64": "1.56.0", "@oxlint/binding-freebsd-x64": "1.56.0", "@oxlint/binding-linux-arm-gnueabihf": "1.56.0", "@oxlint/binding-linux-arm-musleabihf": "1.56.0", "@oxlint/binding-linux-arm64-gnu": "1.56.0", "@oxlint/binding-linux-arm64-musl": "1.56.0", "@oxlint/binding-linux-ppc64-gnu": "1.56.0", "@oxlint/binding-linux-riscv64-gnu": "1.56.0", "@oxlint/binding-linux-riscv64-musl": "1.56.0", "@oxlint/binding-linux-s390x-gnu": "1.56.0", "@oxlint/binding-linux-x64-gnu": "1.56.0", "@oxlint/binding-linux-x64-musl": "1.56.0", "@oxlint/binding-openharmony-arm64": "1.56.0", "@oxlint/binding-win32-arm64-msvc": "1.56.0", "@oxlint/binding-win32-ia32-msvc": "1.56.0", "@oxlint/binding-win32-x64-msvc": "1.56.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.15.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": "bin/oxlint" }, "sha512-Q+5Mj5PVaH/R6/fhMMFzw4dT+KPB+kQW4kaL8FOIq7tfhlnEVp6+3lcWqFruuTNlUo9srZUW3qH7Id4pskeR6g=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
@ -410,6 +576,8 @@
|
||||
|
||||
"postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="],
|
||||
|
||||
"preact": ["preact@10.29.2", "", {}, "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ=="],
|
||||
|
||||
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
@ -422,24 +590,72 @@
|
||||
|
||||
"select2": ["select2@4.0.13", "", {}, "sha512-1JeB87s6oN/TDxQQYCvS5EFoQyvV6eYMZZ0AeA4tdFDYWN3BAGZ8npr17UBFddU0lgAt3H0yjX3X6/ekOj1yjw=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"sightglass": ["sightglass@0.2.6", "", {}, "sha512-t1fgbuhURcWc8VgZk8kJQ3QmmZk3kghDcf0wpsN8I8RaV05IUkc2b195KpGqgocKT/q8+vKk6EcB2c7N2lAd6A=="],
|
||||
|
||||
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
||||
|
||||
"simple-code-frame": ["simple-code-frame@1.3.0", "", { "dependencies": { "kolorist": "^1.6.0" } }, "sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w=="],
|
||||
|
||||
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"stack-trace": ["stack-trace@1.0.0", "", {}, "sha512-H6D7134xi6qONvh7ZHKgviXf+rd3vhGBSvebPZCaUkd8zvQ+7PtDw6CljPTe4cXWNf2IKZGNqw6VJXSb9IgBpA=="],
|
||||
|
||||
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||
|
||||
"std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
|
||||
|
||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||
|
||||
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.2.2", "", {}, "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
|
||||
"tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="],
|
||||
|
||||
"tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||
|
||||
"vite": ["vite@8.0.10", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.10", "rolldown": "1.0.0-rc.17", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "less", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": "bin/vite.js" }, "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw=="],
|
||||
|
||||
"vite-plugin-full-reload": ["vite-plugin-full-reload@1.2.0", "", { "dependencies": { "picocolors": "^1.0.0", "picomatch": "^2.3.1" } }, "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA=="],
|
||||
|
||||
"vite-prerender-plugin": ["vite-prerender-plugin@0.5.13", "", { "dependencies": { "kolorist": "^1.8.0", "magic-string": "0.x >= 0.26.0", "node-html-parser": "^6.1.12", "simple-code-frame": "^1.3.0", "source-map": "^0.7.4", "stack-trace": "^1.0.0-pre2" }, "peerDependencies": { "vite": "5.x || 6.x || 7.x || 8.x" } }, "sha512-IKSpYkzDBsKAxa05naRbj7GvNVMSdww/Z/E89oO3xndz+gWnOBOKOAbEXv7qDhktY/j3vHgJmoV1pPzqU2tx9g=="],
|
||||
|
||||
"vitest": ["vitest@4.1.7", "", { "dependencies": { "@vitest/expect": "4.1.7", "@vitest/mocker": "4.1.7", "@vitest/pretty-format": "4.1.7", "@vitest/runner": "4.1.7", "@vitest/snapshot": "4.1.7", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.7", "@vitest/browser-preview": "4.1.7", "@vitest/browser-webdriverio": "4.1.7", "@vitest/coverage-istanbul": "4.1.7", "@vitest/coverage-v8": "4.1.7", "@vitest/ui": "4.1.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA=="],
|
||||
|
||||
"whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
|
||||
|
||||
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
||||
|
||||
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
|
||||
|
||||
"@prefresh/vite/@rollup/pluginutils": ["@rollup/pluginutils@4.2.1", "", { "dependencies": { "estree-walker": "^2.0.1", "picomatch": "^2.2.2" } }, "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ=="],
|
||||
|
||||
"@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"vite-plugin-full-reload/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"@prefresh/vite/@rollup/pluginutils/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
}
|
||||
}
|
||||
|
||||
192
compose.deploy.yml
Normal file
192
compose.deploy.yml
Normal file
@ -0,0 +1,192 @@
|
||||
# Production Reference Compose stack for phpVMS.
|
||||
#
|
||||
# The `app` service runs the FrankenPHP image with Laravel Octane worker
|
||||
# mode enabled via a `command:` override (the upstream-documented way to
|
||||
# launch Octane with serversideup/php:8.5-frankenphp). Octane keeps the
|
||||
# framework booted across requests, which materially helps Filament /
|
||||
# Livewire-heavy admin paths.
|
||||
#
|
||||
# To fall back to classic FrankenPHP + PHP-worker mode (matches the prior
|
||||
# PHP-FPM request semantics — slower but bulletproof if an Octane bug hits):
|
||||
# delete the `command:` line below and recreate the `app` container. No
|
||||
# rebuild is required.
|
||||
#
|
||||
# Depending on your host, you might have to enter the contents of `.env`
|
||||
# into their environment variable configuration; for example, that's the
|
||||
# case for Railway or Dokploy. Dokploy creates a `.env` from the environment
|
||||
# variables that you set in the interface, so the reading of `.env` is required
|
||||
# regardless
|
||||
#
|
||||
# This compose file also includes a MariaDB and Redis container. You can
|
||||
# run this within this stack, if you really want to. You *must* back up the
|
||||
# Docker volumes listed at the bottom, but I highly recommend against it.
|
||||
# Production deployments should use dedicated services for these components.
|
||||
#
|
||||
# For local development, see `compose.sail.yml` (Laravel Sail).
|
||||
#
|
||||
services:
|
||||
app:
|
||||
# If you want to use the remote image from Docker Hub, comment out this line
|
||||
# or set it to "always" to pull the latest version
|
||||
pull_policy: build
|
||||
|
||||
# Build from the local repository
|
||||
image: ${PHPVMS_IMAGE_NAME:-phpvms/phpvms:latest}
|
||||
|
||||
# Build from the local repository
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.prod
|
||||
args:
|
||||
WWWUSER: "${WWWUSER:-1000}"
|
||||
WWWGROUP: "${WWWGROUP:-1000}"
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
AUTORUN_ENABLED: "true"
|
||||
|
||||
# Enable this if you're having startup trouble...
|
||||
AUTORUN_DEBUG: "false"
|
||||
|
||||
# OPcache tuned for Octane: code is loaded once and stays in memory, so
|
||||
# disable timestamp revalidation (deploys replace the container) and
|
||||
# raise the file cap to cover Filament + module trees.
|
||||
PHP_OPCACHE_ENABLE: "1"
|
||||
PHP_OPCACHE_VALIDATE_TIMESTAMPS: "0"
|
||||
PHP_OPCACHE_MAX_ACCELERATED_FILES: "20000"
|
||||
PHP_OPCACHE_INTERNED_STRINGS_BUFFER: "16"
|
||||
PHP_OPCACHE_JIT: "tracing"
|
||||
PHP_OPCACHE_JIT_BUFFER_SIZE: "64M"
|
||||
|
||||
# For Caddy, no need to change
|
||||
XDG_DATA_HOME: "/var/www/html/storage"
|
||||
XDG_CONFIG_HOME: "/var/www/html/storage"
|
||||
|
||||
# This is the database connection to use
|
||||
DB_CONNECTION: mysql
|
||||
|
||||
DB_HOST: ${DB_HOST:-mysql}
|
||||
DB_PORT: 3306
|
||||
DB_DATABASE: ${DB_DATABASE:-phpvms}
|
||||
DB_USERNAME: ${DB_USERNAME}
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
|
||||
# Delete this line to revert to classic FrankenPHP + PHP-worker mode.
|
||||
command:
|
||||
[
|
||||
"php",
|
||||
"/var/www/html/artisan",
|
||||
"octane:start",
|
||||
"--server=frankenphp",
|
||||
"--host=0.0.0.0",
|
||||
"--workers=auto",
|
||||
"--max-requests=500",
|
||||
]
|
||||
ports:
|
||||
# The image binds to 8000/8443 internally (unprivileged); host ports
|
||||
# 80/443 are mapped at the Compose layer.
|
||||
- "${FORWARD_HTTP_PORT:-80}:8000"
|
||||
- "${FORWARD_HTTPS_PORT:-443}:8443"
|
||||
|
||||
user: "${WWWUSER:-1000}:${WWWGROUP:-1000}"
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./modules:/var/www/html/modules:ro
|
||||
- phpvms_uploads:/var/www/html/public/uploads:rw
|
||||
- phpvms_storage:/var/www/html/storage:rw
|
||||
depends_on:
|
||||
- mariadb
|
||||
- redis
|
||||
healthcheck:
|
||||
# Native health check script for Octane shipped by serversideup/php.
|
||||
test: ["CMD", "healthcheck-octane"]
|
||||
start_period: 30s
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
task:
|
||||
image: ${PHPVMS_IMAGE_NAME:-phpvms/phpvms:latest}
|
||||
user: "${WWWUSER:-1000}:${WWWGROUP:-1000}"
|
||||
restart: unless-stopped
|
||||
command: ["php", "/var/www/html/artisan", "schedule:work"]
|
||||
environment:
|
||||
PHP_OPCACHE_ENABLE: "1"
|
||||
healthcheck:
|
||||
# This is our native health check script for the scheduler
|
||||
test: ["CMD", "healthcheck-schedule"]
|
||||
start_period: 10s
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./modules:/var/www/html/modules:ro
|
||||
- phpvms_uploads:/var/www/html/public/uploads:rw
|
||||
- phpvms_storage:/var/www/html/storage:rw
|
||||
|
||||
queue:
|
||||
image: ${PHPVMS_IMAGE_NAME:-phpvms/phpvms:latest}
|
||||
user: "${WWWUSER:-1000}:${WWWGROUP:-1000}"
|
||||
restart: unless-stopped
|
||||
command: ["php", "/var/www/html/artisan", "queue:work", "--tries=3"]
|
||||
environment:
|
||||
PHP_OPCACHE_ENABLE: "1"
|
||||
healthcheck:
|
||||
# This is our native health check script for the queue
|
||||
test: ["CMD", "healthcheck-queue"]
|
||||
start_period: 10s
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./modules:/var/www/html/modules:ro
|
||||
- phpvms_uploads:/var/www/html/public/uploads:rw
|
||||
- phpvms_storage:/var/www/html/storage:rw
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# BELOW HERE ARE THE CONTAINERS FOR THE DATABASE AND REDIS
|
||||
#
|
||||
# If you are planning to run this in production, I **highly**
|
||||
# recommend using a managed database service like AWS RDS or
|
||||
# Google Cloud SQL. These services offer high availability,
|
||||
# automatic backups, and other features that are not available
|
||||
# with self-hosted solutions.
|
||||
#
|
||||
# ----------------------------------------------------------
|
||||
|
||||
# You can also use PostgreSQL
|
||||
mariadb:
|
||||
hostname: mysql
|
||||
image: mariadb:11.8 # lts
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 3306
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"
|
||||
MYSQL_DATABASE: "${DB_DATABASE}"
|
||||
MYSQL_USER: "${DB_USERNAME}"
|
||||
MYSQL_PASSWORD: "${DB_PASSWORD}"
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "no"
|
||||
volumes:
|
||||
- database:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
start_period: 10s
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
redis:
|
||||
container_name: phpvms-redis
|
||||
image: redis:8.8-alpine3.23
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
retries: 3
|
||||
timeout: 5s
|
||||
|
||||
volumes:
|
||||
database:
|
||||
phpvms_storage:
|
||||
phpvms_uploads:
|
||||
redis:
|
||||
@ -95,7 +95,8 @@
|
||||
"opcodesio/log-viewer": "^3.24",
|
||||
"spatie/laravel-data": "^4.22.1",
|
||||
"jlorente/laravel-data-migrations": "^2.0",
|
||||
"hidehalo/nanoid-php": "^2.0"
|
||||
"hidehalo/nanoid-php": "^2.0",
|
||||
"laravel/octane": "^2.13"
|
||||
},
|
||||
"require-dev": {
|
||||
"filp/whoops": "^2.18.4",
|
||||
@ -152,9 +153,9 @@
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi",
|
||||
"@php artisan ide-helper:generate",
|
||||
"@php artisan ide-helper:meta",
|
||||
".hooks/setup-git-hooks.sh"
|
||||
"@php artisan ide-helper:generate || true",
|
||||
"@php artisan ide-helper:meta || true",
|
||||
".hooks/setup-git-hooks.sh || true"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
|
||||
@ -172,7 +173,7 @@
|
||||
],
|
||||
"bump:minor": [
|
||||
"composer update && composer bump",
|
||||
"npx npm-check-updates -u -t minor && npm install"
|
||||
"bum x npm-check-updates -u -t minor && npm install"
|
||||
],
|
||||
"lint": "pint --parallel",
|
||||
"pint": "pint --parallel",
|
||||
|
||||
776
composer.lock
generated
776
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -57,12 +57,13 @@ return [
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix' => env('DB_PREFIX', ''),
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
Mysql::ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
Mysql::ATTR_PERSISTENT => true, // Critical for Octane
|
||||
]) : [],
|
||||
],
|
||||
|
||||
@ -83,6 +84,7 @@ return [
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
Mysql::ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
Mysql::ATTR_PERSISTENT => true, // Critical for Octane
|
||||
]) : [],
|
||||
],
|
||||
|
||||
|
||||
239
config/octane.php
Normal file
239
config/octane.php
Normal file
@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laravel\Octane\Contracts\OperationTerminated;
|
||||
use Laravel\Octane\Events\RequestHandled;
|
||||
use Laravel\Octane\Events\RequestReceived;
|
||||
use Laravel\Octane\Events\RequestTerminated;
|
||||
use Laravel\Octane\Events\TaskReceived;
|
||||
use Laravel\Octane\Events\TaskTerminated;
|
||||
use Laravel\Octane\Events\TickReceived;
|
||||
use Laravel\Octane\Events\TickTerminated;
|
||||
use Laravel\Octane\Events\WorkerErrorOccurred;
|
||||
use Laravel\Octane\Events\WorkerStarting;
|
||||
use Laravel\Octane\Events\WorkerStopping;
|
||||
use Laravel\Octane\Listeners\CloseMonologHandlers;
|
||||
use Laravel\Octane\Listeners\CollectGarbage;
|
||||
use Laravel\Octane\Listeners\DisconnectFromDatabases;
|
||||
use Laravel\Octane\Listeners\EnsureUploadedFilesAreValid;
|
||||
use Laravel\Octane\Listeners\EnsureUploadedFilesCanBeMoved;
|
||||
use Laravel\Octane\Listeners\FlushOnce;
|
||||
use Laravel\Octane\Listeners\FlushTemporaryContainerInstances;
|
||||
use Laravel\Octane\Listeners\FlushUploadedFiles;
|
||||
use Laravel\Octane\Listeners\ReportException;
|
||||
use Laravel\Octane\Listeners\StopWorkerIfNecessary;
|
||||
use Laravel\Octane\Octane;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Server
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the default "server" that will be used by Octane
|
||||
| when starting, restarting, or stopping your server via the CLI. You
|
||||
| are free to change this to the supported server of your choosing.
|
||||
|
|
||||
| Supported: "roadrunner", "swoole", "frankenphp"
|
||||
|
|
||||
*/
|
||||
|
||||
'server' => env('OCTANE_SERVER', 'frankenphp'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Force HTTPS
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When this configuration value is set to "true", Octane will inform the
|
||||
| framework that all absolute links must be generated using the HTTPS
|
||||
| protocol. Otherwise your links may be generated using plain HTTP.
|
||||
|
|
||||
*/
|
||||
|
||||
'https' => env('OCTANE_HTTPS', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Listeners
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All of the event listeners for Octane's events are defined below. These
|
||||
| listeners are responsible for resetting your application's state for
|
||||
| the next request. You may even add your own listeners to the list.
|
||||
|
|
||||
| NOTE: Octane::prepareApplicationForNextRequest() includes FlushLocaleState,
|
||||
| which is what stops App::setLocale() calls in middleware (e.g.
|
||||
| App\Http\Middleware\SetActiveLanguage) from leaking between requests
|
||||
| served by the same worker. Do NOT drop the spread call below or replace
|
||||
| it with a hand-curated list that omits FlushLocaleState — see the
|
||||
| "Octane Safety Audit" notes for context.
|
||||
|
|
||||
*/
|
||||
|
||||
'listeners' => [
|
||||
WorkerStarting::class => [
|
||||
EnsureUploadedFilesAreValid::class,
|
||||
EnsureUploadedFilesCanBeMoved::class,
|
||||
],
|
||||
|
||||
RequestReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
...Octane::prepareApplicationForNextRequest(),
|
||||
//
|
||||
],
|
||||
|
||||
RequestHandled::class => [
|
||||
//
|
||||
],
|
||||
|
||||
RequestTerminated::class => [
|
||||
// FlushUploadedFiles::class,
|
||||
],
|
||||
|
||||
TaskReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
//
|
||||
],
|
||||
|
||||
TaskTerminated::class => [
|
||||
//
|
||||
],
|
||||
|
||||
TickReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
//
|
||||
],
|
||||
|
||||
TickTerminated::class => [
|
||||
//
|
||||
],
|
||||
|
||||
OperationTerminated::class => [
|
||||
FlushOnce::class,
|
||||
FlushTemporaryContainerInstances::class,
|
||||
// DisconnectFromDatabases::class,
|
||||
// CollectGarbage::class,
|
||||
],
|
||||
|
||||
WorkerErrorOccurred::class => [
|
||||
ReportException::class,
|
||||
StopWorkerIfNecessary::class,
|
||||
],
|
||||
|
||||
WorkerStopping::class => [
|
||||
CloseMonologHandlers::class,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Warm / Flush Bindings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The bindings listed below will either be pre-warmed when a worker boots
|
||||
| or they will be flushed before every new request. Flushing a binding
|
||||
| will force the container to resolve that binding again when asked.
|
||||
|
|
||||
*/
|
||||
|
||||
'warm' => [
|
||||
...Octane::defaultServicesToWarm(),
|
||||
],
|
||||
|
||||
'flush' => [
|
||||
//
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Swoole Tables
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While using Swoole, you may define additional tables as required by the
|
||||
| application. These tables can be used to store data that needs to be
|
||||
| quickly accessed by other workers on the particular Swoole server.
|
||||
|
|
||||
*/
|
||||
|
||||
'tables' => [
|
||||
'example:1000' => [
|
||||
'name' => 'string:1000',
|
||||
'votes' => 'int',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Swoole Cache Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While using Swoole, you may leverage the Octane cache, which is powered
|
||||
| by a Swoole table. You may set the maximum number of rows as well as
|
||||
| the number of bytes per row using the configuration options below.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache' => [
|
||||
'rows' => 1000,
|
||||
'bytes' => 10000,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| File Watching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following list of files and directories will be watched when using
|
||||
| the --watch option offered by Octane. If any of the directories and
|
||||
| files are changed, Octane will automatically reload your workers.
|
||||
|
|
||||
| The list below reflects the phpVMS layout: app/, bootstrap/, config/,
|
||||
| database/, modules/, resources/views/, and routes/ are the directories
|
||||
| a code change can land in. composer.lock and .env are watched so worker
|
||||
| reload picks up dependency and env changes too.
|
||||
|
|
||||
*/
|
||||
|
||||
'watch' => [
|
||||
'app',
|
||||
'bootstrap',
|
||||
'config/**/*.php',
|
||||
'database/**/*.php',
|
||||
'modules/**/*.php',
|
||||
'resources/views/**/*.php',
|
||||
'resources/views/**/*.blade.php',
|
||||
'routes',
|
||||
'composer.lock',
|
||||
'.env',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Garbage Collection Threshold
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When executing long-lived PHP scripts such as Octane, memory can build
|
||||
| up before being cleared by PHP. You can force Octane to run garbage
|
||||
| collection if your application consumes this amount of megabytes.
|
||||
|
|
||||
*/
|
||||
|
||||
'garbage' => 50,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maximum Execution Time
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following setting configures the maximum execution time for requests
|
||||
| being handled by Octane. You may set this value to 0 to indicate that
|
||||
| there isn't a specific time limit on Octane request execution time.
|
||||
|
|
||||
*/
|
||||
|
||||
'max_execution_time' => 30,
|
||||
|
||||
];
|
||||
@ -8,5 +8,4 @@ return [
|
||||
'default' => env('DEFAULT_THEME', 'seven'),
|
||||
'cache' => true,
|
||||
'themes' => [],
|
||||
|
||||
];
|
||||
|
||||
@ -245,35 +245,35 @@ expenses:
|
||||
amount: 100
|
||||
type: F
|
||||
active: 1
|
||||
ref_model: App\Models\Expense
|
||||
ref_model_type: App\Models\Expense
|
||||
- name: Per-Flight (multiplier)
|
||||
amount: 100
|
||||
type: F
|
||||
multiplier: 1
|
||||
active: 1
|
||||
ref_model: App\Models\Expense
|
||||
ref_model_type: App\Models\Expense
|
||||
- name: Per-Flight (multiplier, on airline)
|
||||
airline_id: 1
|
||||
amount: 200
|
||||
type: F
|
||||
multiplier: 1
|
||||
active: 1
|
||||
ref_model: App\Models\Expense
|
||||
ref_model_type: App\Models\Expense
|
||||
- name: A daily fee
|
||||
amount: 800
|
||||
type: D
|
||||
active: 1
|
||||
ref_model: App\Models\Expense
|
||||
ref_model_type: App\Models\Expense
|
||||
- name: A monthly fee
|
||||
amount: 5000
|
||||
type: M
|
||||
active: 1
|
||||
ref_model: App\Models\Expense
|
||||
ref_model_type: App\Models\Expense
|
||||
- name: Catering
|
||||
amount: 1000
|
||||
type: F
|
||||
active: 1
|
||||
ref_model: App\Models\Subfleet
|
||||
ref_model_type: App\Models\Subfleet
|
||||
ref_model_id: 1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
@ -281,7 +281,7 @@ expenses:
|
||||
amount: 1000
|
||||
type: D
|
||||
active: 1
|
||||
ref_model: App\Models\Subfleet
|
||||
ref_model_type: App\Models\Subfleet
|
||||
ref_model_id: 1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
@ -289,7 +289,7 @@ expenses:
|
||||
amount: 1000
|
||||
type: D
|
||||
active: 1
|
||||
ref_model: App\Models\Aircraft
|
||||
ref_model_type: App\Models\Aircraft
|
||||
ref_model_id: 1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
@ -861,7 +861,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Pilot Payment @ 50"
|
||||
tags: "pilot_pay"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: "2018-03-06 12:34:15"
|
||||
updated_at: "2018-03-06 12:34:15"
|
||||
@ -874,7 +874,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Expense: Per-Flight (no muliplier)"
|
||||
tags: "expense"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: "2018-03-06 12:34:15"
|
||||
updated_at: "2018-03-06 12:34:15"
|
||||
@ -887,7 +887,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Fares Y300; price: 200, cost: 0"
|
||||
tags: "fare"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: "2018-03-06 12:34:15"
|
||||
updated_at: "2018-03-06 12:34:15"
|
||||
@ -900,7 +900,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Expense: Per-Flight (multiplier, on airline)"
|
||||
tags: "expense"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: "2018-03-06 12:34:15"
|
||||
updated_at: "2018-03-06 12:34:15"
|
||||
@ -913,7 +913,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Expense: Per-Flight (multiplier)"
|
||||
tags: "expense"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: "2018-03-06 12:34:15"
|
||||
updated_at: "2018-03-06 12:34:15"
|
||||
@ -926,7 +926,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Subfleet Expense: Catering (747-43X RB211-524G)"
|
||||
tags: "subfleet"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: "2018-03-06 12:34:15"
|
||||
updated_at: "2018-03-06 12:34:15"
|
||||
@ -939,7 +939,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Fares F10; price: 1000, cost: 0"
|
||||
tags: "fare"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: "2018-03-06 12:34:15"
|
||||
updated_at: "2018-03-06 12:34:15"
|
||||
@ -952,7 +952,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Pilot Payment @ 50"
|
||||
tags: "pilot_pay"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
@ -965,7 +965,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Fares B10; price: 1100, cost: 0"
|
||||
tags: "fare"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
@ -978,7 +978,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Ground Handling"
|
||||
tags: "ground_handling"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
@ -992,7 +992,7 @@ journal_transactions:
|
||||
currency: USD
|
||||
memo: "Subfleet 744-3X-RB211: Block Time Cost"
|
||||
tags: "subfleet"
|
||||
ref_model: App\Models\Pirep
|
||||
ref_model_type: App\Models\Pirep
|
||||
ref_model_id: pirepid_1
|
||||
created_at: now
|
||||
updated_at: now
|
||||
|
||||
@ -1,133 +0,0 @@
|
||||
# This Docker Compose file is an example of what a setup for running phpVMS in production could look like.
|
||||
# It is based on the image created in the Dockerfile. You can find more examples in phpvms' documentation.
|
||||
services:
|
||||
app:
|
||||
container_name: phpvms-app
|
||||
user: "${WWWUSER:-1000}:${WWWGROUP:-1000}"
|
||||
image: ghcr.io/phpvms/phpvms:main
|
||||
hostname: app
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PHP_OPCACHE_ENABLE: 1
|
||||
# some basic laravel stuff
|
||||
AUTORUN_ENABLED: 1
|
||||
env_file: .env
|
||||
volumes:
|
||||
- phpvms_assets:/var/www/html/public/build:rw
|
||||
- ./modules:/var/www/html/modules:ro
|
||||
- phpvms_uploads:/var/www/html/public/uploads:rw
|
||||
- phpvms_storage:/var/www/html/storage:rw
|
||||
depends_on:
|
||||
- caddy
|
||||
- mariadb
|
||||
- redis
|
||||
networks:
|
||||
- phpvms
|
||||
|
||||
task:
|
||||
container_name: phpvms-task
|
||||
user: "${WWWUSER:-1000}:${WWWGROUP:-1000}"
|
||||
image: ghcr.io/phpvms/phpvms:main
|
||||
restart: unless-stopped
|
||||
command: ["php", "/var/www/html/artisan", "schedule:work"]
|
||||
environment:
|
||||
PHP_OPCACHE_ENABLE: 1
|
||||
healthcheck:
|
||||
# This is our native healthcheck script for the scheduler
|
||||
test: ["CMD", "healthcheck-schedule"]
|
||||
start_period: 10s
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./modules:/var/www/html/modules:ro
|
||||
- phpvms_uploads:/var/www/html/public/uploads:rw
|
||||
- phpvms_storage:/var/www/html/storage:rw
|
||||
networks:
|
||||
- phpvms
|
||||
|
||||
queue:
|
||||
container_name: phpvms-queue
|
||||
user: "${WWWUSER:-1000}:${WWWGROUP:-1000}"
|
||||
image: ghcr.io/phpvms/phpvms:main
|
||||
restart: unless-stopped
|
||||
command: ["php", "/var/www/html/artisan", "queue:work", "--tries=3"]
|
||||
environment:
|
||||
PHP_OPCACHE_ENABLE: 1
|
||||
healthcheck:
|
||||
# This is our native healthcheck script for the queue
|
||||
test: ["CMD", "healthcheck-queue"]
|
||||
start_period: 10s
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./modules:/var/www/html/modules:ro
|
||||
- phpvms_uploads:/var/www/html/public/uploads:rw
|
||||
- phpvms_storage:/var/www/html/storage:rw
|
||||
networks:
|
||||
- phpvms
|
||||
|
||||
caddy:
|
||||
container_name: phpvms-caddy
|
||||
image: caddy:2
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
ports:
|
||||
- "${FORWARD_HTTP_PORT:-80}:80"
|
||||
- "${FORWARD_HTTPS_PORT:-443}:443"
|
||||
volumes:
|
||||
- ./public/:/var/www/html/public
|
||||
- phpvms_assets:/var/www/html/public/build
|
||||
- ./resources/docker/Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_config:/config
|
||||
- caddy_data:/data
|
||||
networks:
|
||||
- default
|
||||
- phpvms
|
||||
|
||||
mariadb:
|
||||
container_name: phpvms-mariadb
|
||||
image: mariadb:11
|
||||
restart: unless-stopped
|
||||
# If someone need to access db from the outside
|
||||
ports:
|
||||
- "${FORWARD_DB_PORT:-3306}:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"
|
||||
MYSQL_DATABASE: "${DB_DATABASE}"
|
||||
MYSQL_USER: "${DB_USERNAME}"
|
||||
MYSQL_PASSWORD: "${DB_PASSWORD}"
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "no"
|
||||
volumes:
|
||||
- mariadb:/var/lib/mysql
|
||||
networks:
|
||||
- phpvms
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
start_period: 10s
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
redis:
|
||||
container_name: phpvms-redis
|
||||
image: redis:alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis:/data
|
||||
networks:
|
||||
- phpvms
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
retries: 3
|
||||
timeout: 5s
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
mariadb:
|
||||
phpvms_storage:
|
||||
phpvms_uploads:
|
||||
phpvms_assets:
|
||||
redis:
|
||||
|
||||
networks:
|
||||
phpvms:
|
||||
driver: bridge
|
||||
@ -1,64 +0,0 @@
|
||||
<code_scheme name="phpVMS" version="173">
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="ALIGN_KEY_VALUE_PAIRS" value="true" />
|
||||
<option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" />
|
||||
<option name="ALIGN_PHPDOC_COMMENTS" value="true" />
|
||||
<option name="CONCAT_SPACES" value="false" />
|
||||
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
||||
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
|
||||
<option name="THROWS_WEIGHT" value="1" />
|
||||
<option name="RETURN_WEIGHT" value="2" />
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="ELSE_IF_STYLE" value="COMBINE" />
|
||||
<option name="FIELDS_DEFAULT_VISIBILITY" value="protected" />
|
||||
<option name="VARIABLE_NAMING_STYLE" value="CAMEL_CASE" />
|
||||
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
|
||||
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
|
||||
<option name="SPACES_WITHIN_SHORT_ECHO_TAGS" value="false" />
|
||||
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
|
||||
<option name="PHPDOC_USE_FQCN" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="LINE_COMMENT_ADD_SPACE" value="true" />
|
||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="0" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
||||
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="true" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
|
||||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<option name="WRAP_ON_TYPING" value="0" />
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
94
justfile
94
justfile
@ -1,94 +0,0 @@
|
||||
# phpVMS task runner
|
||||
#
|
||||
# Install just: brew install just (or cargo install just)
|
||||
# List recipes: just --list
|
||||
|
||||
set shell := ["bash", "-cu"]
|
||||
|
||||
# Default: list all recipes
|
||||
default:
|
||||
@just --list
|
||||
|
||||
# ── Frontend ────────────────────────────────────────────────────────────────
|
||||
|
||||
# Vite dev server (theme + admin map bundle, HMR)
|
||||
dev:
|
||||
npm run dev
|
||||
|
||||
# Watch Alpine components (esbuild --dev, inline sourcemaps)
|
||||
dev-components:
|
||||
npm run dev:components
|
||||
|
||||
# Production build: Vite (theme + admin) + esbuild (Alpine components) + Filament asset publish
|
||||
build:
|
||||
npm run build
|
||||
php artisan filament:assets
|
||||
|
||||
# Vite only — theme + admin map bundle
|
||||
build-vite:
|
||||
npm run build
|
||||
|
||||
# esbuild only — Filament AlpineComponent bundles
|
||||
build-components:
|
||||
npm run build:components
|
||||
php artisan filament:assets
|
||||
|
||||
# Publish Filament assets into /public (AlpineComponents, plugin CSS/JS)
|
||||
publish:
|
||||
php artisan filament:assets
|
||||
|
||||
# ── Linting / Formatting ────────────────────────────────────────────────────
|
||||
|
||||
# JS lint (oxlint)
|
||||
lint-js:
|
||||
npm run lint
|
||||
|
||||
lint-js-fix:
|
||||
npm run lint:fix
|
||||
|
||||
# JS format (oxfmt)
|
||||
fmt-js:
|
||||
npm run fmt
|
||||
|
||||
fmt-js-check:
|
||||
npm run fmt:check
|
||||
|
||||
# PHP format (Pint, dirty files only)
|
||||
fmt-php:
|
||||
vendor/bin/pint --dirty
|
||||
|
||||
fmt-php-check:
|
||||
vendor/bin/pint --test
|
||||
|
||||
# ── Static Analysis ─────────────────────────────────────────────────────────
|
||||
|
||||
# PHPStan / Larastan (level 5)
|
||||
stan:
|
||||
vendor/bin/phpstan analyse
|
||||
|
||||
# Rector dry-run (no writes)
|
||||
rector-dry:
|
||||
vendor/bin/rector --dry-run
|
||||
|
||||
# Rector apply (writes changes)
|
||||
rector:
|
||||
vendor/bin/rector
|
||||
|
||||
# ── Testing ─────────────────────────────────────────────────────────────────
|
||||
|
||||
# Full Pest suite
|
||||
test:
|
||||
composer test
|
||||
|
||||
# Filter tests: `just test-filter PirepTest`
|
||||
test-filter filter:
|
||||
php artisan test --compact --filter={{filter}}
|
||||
|
||||
# ── Pre-PR Checklist (matches AGENTS.md) ────────────────────────────────────
|
||||
|
||||
# Run all four gates: pint, phpstan, pest, rector
|
||||
check:
|
||||
composer pint --test
|
||||
vendor/bin/phpstan analyse
|
||||
composer test
|
||||
vendor/bin/rector --dry-run
|
||||
8
mise.toml
Normal file
8
mise.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[env]
|
||||
BUILDKIT_HOST = "docker-container://buildkit"
|
||||
|
||||
[tasks.setup]
|
||||
run = ["mise install", "mise run run-buildkit-container || true"]
|
||||
|
||||
[tasks.run-buildkit-container]
|
||||
run = "docker run --rm --privileged -d --name buildkit -e BUILDKIT_DEBUG=1 moby/buildkit:latest"
|
||||
10
package.json
10
package.json
@ -2,17 +2,17 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev:components": "node bin/build.js --dev",
|
||||
"build": "vite build && node bin/build.js",
|
||||
"build:components": "node bin/build.js",
|
||||
"dev": "bun x --bun vite",
|
||||
"dev:components": "bun bin/build.js --dev",
|
||||
"build": "bun x --bun vite build && bun bin/build.js",
|
||||
"build:components": "bun bin/build.js",
|
||||
"clean": "rm -rf public/build resources/js/dist",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt",
|
||||
"fmt:check": "oxfmt --check",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run"
|
||||
"test": "bun x --bun vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@date-fns/tz": "^1.5.0",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
railpack.json
Normal file
6
railpack.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://schema.railpack.com",
|
||||
"deploy": {
|
||||
"startCommand": "php artisan octane:start --server=frankenphp --host=0.0.0.0 --port=${FORWARD_HTTP_PORT:-8080}"
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
{$CADDY_DOMAIN} {
|
||||
root * /var/www/html/public
|
||||
|
||||
encode zstd gzip
|
||||
|
||||
file_server
|
||||
|
||||
php_fastcgi app:9000
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
rm -rf ./public/build/*
|
||||
cp -R ./build/* ./public/build
|
||||
rm -rf ./build
|
||||
48
tests/Feature/Octane/ActivityLoggingResetTest.php
Normal file
48
tests/Feature/Octane/ActivityLoggingResetTest.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Http\Middleware\DisableActivityLoggingByDefault;
|
||||
use App\Http\Middleware\EnableActivityLogging;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Spatie\Activitylog\ActivityLogStatus;
|
||||
|
||||
/**
|
||||
* Octane keeps the framework booted across requests on the same worker.
|
||||
* The DisableActivityLoggingByDefault middleware exists to reset logging
|
||||
* to "off" at the start of every request, so an earlier request's
|
||||
* EnableActivityLogging call does not leak into the next request.
|
||||
*
|
||||
* @group octane
|
||||
*/
|
||||
pest()->group('octane');
|
||||
|
||||
test('disable middleware turns activity logging off at request start', function (): void {
|
||||
activity()->enableLogging();
|
||||
|
||||
expect(app(ActivityLogStatus::class)->disabled())->toBeFalse();
|
||||
|
||||
(new DisableActivityLoggingByDefault())->handle(Request::create('/'), fn (): Response => new Response());
|
||||
|
||||
expect(app(ActivityLogStatus::class)->disabled())->toBeTrue();
|
||||
});
|
||||
|
||||
test('logging from request A does not leak into request B on the same worker', function (): void {
|
||||
$disable = new DisableActivityLoggingByDefault();
|
||||
$enable = new EnableActivityLogging();
|
||||
|
||||
// Request A — enabled via EnableActivityLogging.
|
||||
$disable->handle(Request::create('/admin'), fn (): Response => $enable->handle(Request::create('/admin'), function (): Response {
|
||||
expect(app(ActivityLogStatus::class)->disabled())->toBeFalse();
|
||||
|
||||
return new Response();
|
||||
}));
|
||||
|
||||
// Request B — same booted application, no EnableActivityLogging this time.
|
||||
$disable->handle(Request::create('/'), function (): Response {
|
||||
expect(app(ActivityLogStatus::class)->disabled())->toBeTrue();
|
||||
|
||||
return new Response();
|
||||
});
|
||||
});
|
||||
43
tests/Feature/Octane/ModuleLinkStabilityTest.php
Normal file
43
tests/Feature/Octane/ModuleLinkStabilityTest.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Services\ModuleService;
|
||||
|
||||
/**
|
||||
* ModuleService used to hold its admin/frontend link arrays as `protected
|
||||
* static array`. Static state survives across requests on the same Octane
|
||||
* worker, which is fine when registration happens once at module boot but
|
||||
* was a footgun for any future per-request caller. The arrays are now
|
||||
* instance state on a shared singleton; this test asserts that fetching
|
||||
* the link list multiple times in a row does not grow it.
|
||||
*
|
||||
* @group octane
|
||||
*/
|
||||
pest()->group('octane');
|
||||
|
||||
test('admin link count is stable across repeated reads', function (): void {
|
||||
/** @var ModuleService $service */
|
||||
$service = app(ModuleService::class);
|
||||
|
||||
$service->addAdminLink('Sample', '/admin/sample', 'pe-7s-note');
|
||||
|
||||
$baseline = count($service->getAdminLinks());
|
||||
|
||||
foreach (range(1, 5) as $_) {
|
||||
expect(count($service->getAdminLinks()))->toBe($baseline);
|
||||
}
|
||||
});
|
||||
|
||||
test('frontend link count is stable across repeated reads', function (): void {
|
||||
/** @var ModuleService $service */
|
||||
$service = app(ModuleService::class);
|
||||
|
||||
$service->addFrontendLink('Sample', '/sample', 'bi bi-people', true);
|
||||
|
||||
$baseline = count($service->getFrontendLinks(true));
|
||||
|
||||
foreach (range(1, 5) as $_) {
|
||||
expect(count($service->getFrontendLinks(true)))->toBe($baseline);
|
||||
}
|
||||
});
|
||||
38
tests/Feature/Octane/ThemeResetTest.php
Normal file
38
tests/Feature/Octane/ThemeResetTest.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Http\Middleware\SetActiveTheme;
|
||||
use Igaster\LaravelTheme\Facades\Theme;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
/**
|
||||
* SetActiveTheme used to early-return on admin/api/install paths, leaving
|
||||
* whatever theme the previous frontend request had set on the long-lived
|
||||
* Theme singleton. Under Octane that's a per-worker leak. The middleware
|
||||
* now always sets a baseline theme.
|
||||
*
|
||||
* @group octane
|
||||
*/
|
||||
pest()->group('octane');
|
||||
|
||||
test('admin path does not inherit theme from a prior frontend request', function (): void {
|
||||
updateSetting('general.theme', 'seven');
|
||||
|
||||
$middleware = new SetActiveTheme();
|
||||
|
||||
// Request 1: frontend route — middleware sets the configured theme.
|
||||
$middleware->handle(Request::create('/'), fn (): Response => new Response());
|
||||
|
||||
$themeAfterFrontend = Theme::get();
|
||||
|
||||
// Request 2: admin route — same booted Theme singleton.
|
||||
$middleware->handle(Request::create('/admin'), fn (): Response => new Response());
|
||||
$themeAfterAdmin = Theme::get();
|
||||
|
||||
// Both should equal the configured default. The bug we're guarding
|
||||
// against was: request 2 inherits request 1's theme implicitly because
|
||||
// the middleware short-circuits on /admin and never resets the singleton.
|
||||
expect($themeAfterAdmin)->toBe($themeAfterFrontend);
|
||||
});
|
||||
26
version.json
26
version.json
@ -1,26 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
|
||||
"version": "8.0-alpha.{height}",
|
||||
"publicReleaseRefSpec": ["^refs/heads/main$", "^refs/heads/releases/v\\d+(?:\\.\\d+)?$"],
|
||||
"release": {
|
||||
"branchName": "releases/v{version}",
|
||||
"versionIncrement": "build",
|
||||
"firstUnstableTag": "alpha"
|
||||
},
|
||||
"cloudBuild": {
|
||||
"buildNumber": {
|
||||
"enabled": true
|
||||
},
|
||||
"setVersionVariables": true,
|
||||
"setAllVariables": true
|
||||
},
|
||||
"pathFilters": [
|
||||
":!/.config",
|
||||
":!/.github",
|
||||
":!/build",
|
||||
":!/modules",
|
||||
":!/storage",
|
||||
":!/tests",
|
||||
":!/version.json"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user