diff --git a/.cursor/rules/vuetify-standards.mdc b/.cursor/rules/vuetify-standards.mdc
index 2560d4746..5b4ef81ad 100644
--- a/.cursor/rules/vuetify-standards.mdc
+++ b/.cursor/rules/vuetify-standards.mdc
@@ -24,7 +24,7 @@ globs: vue/src/js/**/*.js, vue/src/js/**/*.vue
- Implement Vue.js composition API for state management and component logic.
- Use Vite for asset bundling and development server.
- Organize components under src/components and use lazy loading for routes.
- - Validate forms using Vuelidate and enhance UI with Vuetify components.
+ - Validate forms using useValidation (vue/src/js/hooks/useValidation.js) and enhance UI with Vuetify components.
Key points
- Follow Vue.js's component-driven design for clear separation of business logic, data, and presentation layers.
@@ -33,11 +33,11 @@ globs: vue/src/js/**/*.js, vue/src/js/**/*.vue
- Use Vite for asset bundling and development server.
- Organize components under vue/src/components
- Organize composables under vue/src/hooks
- - Organize behaviors under vue/src/behaviors
+ - Organize composables under vue/src/js/hooks (behaviors folder is deprecated)
- Organize directives under vue/src/directives
- Organize utilities under vue/src/utils
- Organize stores under vue/src/stores
- - Validate forms using Vuelidate and use vue/src/hooks/useValidation.js
+ - Validate forms using vue/src/js/hooks/useValidation.js (useValidation composable)
- Use vue/src/hooks/useInput hook on input components under vue/src/components/inputs
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..f3c47a728
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{js,vue}]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index c2a82e45a..bb2b94151 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -115,5 +115,5 @@ jobs:
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
# composer update --prefer-dist --no-interaction
- name: Execute Laravel tests
- run: composer test
+ run: composer test:fast
diff --git a/.gitignore b/.gitignore
index 109dfa61f..242ee29b0 100755
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,4 @@ all-coverage-clover.xml
# AI
.modulai/
+/tmp-modules/
diff --git a/AGENTS.md b/AGENTS.md
index 86cde48e1..ed46a840c 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -10,6 +10,8 @@ You are an expert in Modularity package development. This is the unusualify/modu
src/ # Package source code (work here)
├── Console # Artisan commands
+├── Hydrates/ # Schema hydrators (InputHydrator → *Hydrate)
+│ └── Inputs/ # Input-specific hydrates (type → schema)
├── Http/Controllers/ # Controllers
├── Providers/ # Service providers
├── Repositories/ # Repository pattern
@@ -18,7 +20,9 @@ src/ # Package source code (work here)
└── Entities/ # Models
vue/src/ # Frontend source
├── js/components/ # Vuetify components
-└── js/composables/ # Vue composables
+├── js/hooks/ # Vue composables
+├── js/utils/ # Utilities (helpers, schema, etc.)
+└── js/store/ # Vuex store
## PATTERNS TO ALWAYS USE
2. **Use Traits**: ManageMedias, HasMedias, MediasTrait etc.
@@ -52,5 +56,63 @@ vue/src/ # Frontend source
- ❌ Hard-coded paths (use config)
- ❌ Options API in Vue (use Composition API)
- ❌ Plain HTML (use Vuetify components)
+- ❌ window.__* helpers in new code (use import from @/utils/helpers)
+
+## HELPERS
+- Prefer `import { isObject, dataGet } from '@/utils/helpers'` over `window.__isObject`, `window.__data_get`
+- window.__* is deprecated; kept for backward compatibility during migration
+
+---
+
+## HYDRATE ↔ INPUT ADAPTER
+
+The backend (PHP Hydrates) and frontend (Vue Inputs) communicate via a **schema contract**. Hydrates produce schema; Input components consume it.
+
+### Data Flow
+
+```
+Module config (type: 'checklist') → InputHydrator → ChecklistHydrate → schema { type: 'input-checklist', ... }
+ ↓
+FormBase/FormBaseField → mapTypeToComponent('input-checklist') → VInputChecklist (Checklist.vue)
+```
+
+### Naming Convention
+
+| Hydrate class | Config type | Output type (schema) | Vue component | File |
+|--------------------|-------------|----------------------|-----------------|-------------------|
+| ChecklistHydrate | checklist | input-checklist | VInputChecklist | Checklist.vue |
+| TaggerHydrate | tagger | input-tagger | VInputTagger | Tagger.vue |
+| SelectHydrate | select | select (or input-select-scroll) | v-select | (Vuetify) |
+| FileHydrate | file | input-file | VInputFile | File.vue |
+| ImageHydrate | image | input-image | VInputImage | Image.vue |
+| ... | ... | input-{kebab} | VInput{Studly} | {Studly}.vue |
+
+- **Hydrate**: `studlyName($input['type']) . 'Hydrate'` → e.g. `checklist` → `ChecklistHydrate`
+- **Output type**: Hydrate sets `$input['type'] = 'input-{kebab}'` (e.g. `input-checklist`)
+- **Vue component**: `registerComponents(..., 'inputs', 'VInput')` → `Checklist.vue` → `VInputChecklist`
+- **Resolution**: `mapTypeToComponent('input-checklist')` → `v-input-checklist` (kebab of VInputChecklist)
+
+### When Adding a New Input
+
+1. **PHP**: Create `src/Hydrates/Inputs/{Studly}Hydrate.php` extending `InputHydrate`
+ - Set `$input['type'] = 'input-{kebab}'` in `hydrate()`
+ - Define `$requirements` for default schema keys
+2. **Vue**: Create `vue/src/js/components/inputs/{Studly}.vue`
+ - Use `useInput`, `makeInputProps`, `makeInputEmits` from `@/hooks`
+ - Component registers as `VInput{Studly}` via `includeFormInputs` glob
+3. **Registry** (optional): Add to `hydrateTypeMap` in `registry.js` for explicit mapping
+
+### Schema Contract
+
+Vue inputs expect schema props via `obj.schema` or `boundProps`:
+
+- **Common**: `name`, `label`, `default`, `rules`, `items`, `itemValue`, `itemTitle`
+- **Selectable**: `cascadeKey`, `cascades`, `repository`, `endpoint`
+- **Files**: `accept`, `maxFileSize`, `translated`, `max`
+- **Hydrate-only** (stripped before frontend): `route`, `model`, `repository`, `cascades`, `connector`
+
+### Hydrate Types → Vue Components
+
+See `vue/src/js/components/inputs/registry.js` → `hydrateTypeMap` for the full mapping.
Always ask for clarification if the request is ambiguous.
diff --git a/FRONTEND_SUGGESTIONS.md b/FRONTEND_SUGGESTIONS.md
new file mode 100644
index 000000000..a5f18dcfe
--- /dev/null
+++ b/FRONTEND_SUGGESTIONS.md
@@ -0,0 +1,91 @@
+# Frontend Enhancement Suggestions
+
+For your review when you're back. Respond to what you'd like to prioritize.
+
+---
+
+## 1. Options API → Composition API Migration
+
+**Current**: ~90% of components use Options API; AGENTS.md mandates Composition API.
+
+**Suggestions**:
+- Migrate high-traffic components first: Form.vue, Auth.vue, Datatable.vue
+- Add ESLint rule to enforce Composition API for new components
+- Create migration script for incremental conversion
+
+---
+
+## 2. Replace Mixins with Composables
+
+**Current**: Mixins (Locale, Modal, Input, MediaLibrary) overlap with composables.
+
+**Suggestions**:
+- Audit `grep -r "mixins:" vue/src`
+- Replace each mixin with equivalent composable
+- ~~Deprecate `mixins/` folder~~ ✓ Done – refactored to hooks, folder removed
+
+---
+
+## 3. Complete CustomFormBase Extraction
+
+**Current**: Input Registry and InputRenderer exist; CustomFormBase still has inline type blocks.
+
+**Suggestions**:
+- Extract each schema type (preview, title, radio, array, wrap/group, etc.) into `schema-types/Input*.vue`
+- CustomFormBase becomes a loop over `` with slot passthrough
+- Reduces CustomFormBase from ~1400 to ~200 lines
+
+---
+
+## 4. Add CSRF Meta to Vitest Setup
+
+**Current**: Some tests fail because `document.querySelector('meta[name="csrf-token"]')` returns null in jsdom.
+
+**Suggestions**:
+- Add `` to vitest-setup jsdom
+- Or mock Document in affected tests
+
+---
+
+## 5. TypeScript Migration
+
+**Current**: All .js and .vue; no type safety.
+
+**Suggestions**:
+- Migrate utils/helpers.js to TypeScript first
+- Add types for store, API responses
+- Incremental migration for new files
+
+---
+
+## 6. Labs Component Flag
+
+**Current**: `components/labs/` mixed with production.
+
+**Suggestions**:
+- Add `VUE_ENABLE_LABS=true` to conditionally load labs in build
+- Exclude labs from production bundle when flag is false
+
+---
+
+## 7. Replace window.__* Usage in Componables
+
+**Current**: Many composables still use `window.__isObject`, `window.__isset`, etc.
+
+**Suggestions**:
+- Replace with `import { isObject, isset } from '@/utils/helpers'` in composables
+- Remove window.__* assignments from init.js once migration is complete
+
+---
+
+## 8. Vuex → Pinia Migration
+
+**Current**: Vuex 4; Pinia recommended for Vue 3.
+
+**Suggestions**:
+- See docs/PINIA_MIGRATION.md
+- Plan for v4.x; create Pinia stores alongside Vuex
+
+---
+
+**Priority order** (suggested): 4 (fix tests) → 7 (finish helpers migration) → 1 (Composition API) → 3 (CustomFormBase extraction) → 2 (mixins) → 5 (TypeScript) → 6 (labs) → 8 (Pinia)
diff --git a/composer.json b/composer.json
index d96b130f2..7e975f6a3 100755
--- a/composer.json
+++ b/composer.json
@@ -67,12 +67,13 @@
"wikimedia/composer-merge-plugin": "^2.1"
},
"require-dev": {
+ "brianium/paratest": "^7.4",
"doctrine/dbal": "^3.9",
"fakerphp/faker": "^1.9.1",
- "laravel/pint": "^1.18",
- "orchestra/testbench": "^7.0|^8.23.4|^9.0",
"larastan/larastan": "^2.0",
+ "laravel/pint": "^1.18",
"laravel/sanctum": "^3.3",
+ "orchestra/testbench": "^7.0|^8.23.4|^9.0",
"phpunit/phpunit": "^9.0|^10.0.7|^11.0"
},
"extra": {
@@ -109,13 +110,37 @@
"Unusualify\\Modularity\\Tests\\": "tests",
"Workbench\\App\\": "workbench/app/",
"Workbench\\Database\\Factories\\": "workbench/database/factories/",
- "Workbench\\Database\\Seeders\\": "workbench/database/seeders/"
+ "Workbench\\Database\\Seeders\\": "workbench/database/seeders/",
+ "Modules\\": "tests/Stubs/Modules",
+ "TestModules\\": "test-modules"
}
},
"scripts": {
"test": "vendor/bin/phpunit --stop-on-defect --no-coverage",
+ "test-clean": [
+ "@php -r \"foreach(glob('vendor/orchestra/testbench-core/laravel/bootstrap/cache/*.php') as \\$f) unlink(\\$f);\"",
+ "echo 'Test cache cleared'"
+ ],
+ "test:parallel": [
+ "@test-clean",
+ "@php -d memory_limit=1G vendor/bin/paratest --processes=2 --stop-on-defect --no-coverage"
+ ],
+ "test:parallel:coverage": [
+ "@test-clean",
+ "@php -d memory_limit=1G vendor/bin/paratest --processes=2 --stop-on-defect --coverage-html coverage-html"
+ ],
+ "test:fast": [
+ "@test-clean",
+ "@php -d memory_limit=1G vendor/bin/paratest --processes=4 --stop-on-defect --no-coverage"
+ ],
+ "test:fast:coverage": [
+ "@test-clean",
+ "@php -d memory_limit=1G vendor/bin/paratest --processes=4 --stop-on-defect --coverage-html coverage-html"
+ ],
"test:brokers": "vendor/bin/phpunit --stop-on-defect --no-coverage tests/Brokers",
"test:events": "vendor/bin/phpunit --stop-on-defect --no-coverage tests/Events",
+ "test:exceptions": "vendor/bin/phpunit --stop-on-defect --no-coverage tests/Exceptions",
+ "test:facades": "vendor/bin/phpunit --stop-on-defect --no-coverage tests/Facades",
"test:helpers": "vendor/bin/phpunit --stop-on-defect --no-coverage tests/Helpers",
"test:http": "vendor/bin/phpunit --stop-on-defect --no-coverage tests/Http",
"test:models": "vendor/bin/phpunit --stop-on-defect --no-coverage tests/Models",
diff --git a/composer.lock b/composer.lock
index b39b082d9..2ebacf589 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "87ef68c3738fd2c99aa5ca79c8b6e393",
+ "content-hash": "3a4e863c64d14a2fb43830ee4dc8b21b",
"packages": [
{
"name": "astrotomic/laravel-translatable",
@@ -4057,16 +4057,16 @@
},
{
"name": "oobook/manage-eloquent",
- "version": "v1.2.2",
+ "version": "v1.2.4",
"source": {
"type": "git",
"url": "https://github.com/OoBook/manage-eloquent.git",
- "reference": "110fc690ca8d9e4044032c3fbefc4d02ddcf8aa6"
+ "reference": "67745f2c60d0b69c724ecc4f28aed1842edb0af4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/OoBook/manage-eloquent/zipball/110fc690ca8d9e4044032c3fbefc4d02ddcf8aa6",
- "reference": "110fc690ca8d9e4044032c3fbefc4d02ddcf8aa6",
+ "url": "https://api.github.com/repos/OoBook/manage-eloquent/zipball/67745f2c60d0b69c724ecc4f28aed1842edb0af4",
+ "reference": "67745f2c60d0b69c724ecc4f28aed1842edb0af4",
"shasum": ""
},
"require": {
@@ -4115,9 +4115,9 @@
],
"support": {
"issues": "https://github.com/OoBook/manage-eloquent/issues",
- "source": "https://github.com/OoBook/manage-eloquent/tree/v1.2.2"
+ "source": "https://github.com/OoBook/manage-eloquent/tree/v1.2.4"
},
- "time": "2025-05-10T11:40:06+00:00"
+ "time": "2026-01-12T22:41:19+00:00"
},
{
"name": "oobook/post-redirector",
@@ -5568,16 +5568,16 @@
},
{
"name": "symfony/console",
- "version": "v6.4.25",
+ "version": "v6.4.32",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "273fd29ff30ba0a88ca5fb83f7cf1ab69306adae"
+ "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/273fd29ff30ba0a88ca5fb83f7cf1ab69306adae",
- "reference": "273fd29ff30ba0a88ca5fb83f7cf1ab69306adae",
+ "url": "https://api.github.com/repos/symfony/console/zipball/0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3",
+ "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3",
"shasum": ""
},
"require": {
@@ -5642,7 +5642,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v6.4.25"
+ "source": "https://github.com/symfony/console/tree/v6.4.32"
},
"funding": [
{
@@ -5662,7 +5662,7 @@
"type": "tidelift"
}
],
- "time": "2025-08-22T10:21:53+00:00"
+ "time": "2026-01-13T08:45:59+00:00"
},
{
"name": "symfony/css-selector",
@@ -7146,16 +7146,16 @@
},
{
"name": "symfony/process",
- "version": "v6.4.25",
+ "version": "v6.4.33",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "6be2f0c9ab3428587c07bed03aa9e3d1b823c6c8"
+ "reference": "c46e854e79b52d07666e43924a20cb6dc546644e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/6be2f0c9ab3428587c07bed03aa9e3d1b823c6c8",
- "reference": "6be2f0c9ab3428587c07bed03aa9e3d1b823c6c8",
+ "url": "https://api.github.com/repos/symfony/process/zipball/c46e854e79b52d07666e43924a20cb6dc546644e",
+ "reference": "c46e854e79b52d07666e43924a20cb6dc546644e",
"shasum": ""
},
"require": {
@@ -7187,7 +7187,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v6.4.25"
+ "source": "https://github.com/symfony/process/tree/v6.4.33"
},
"funding": [
{
@@ -7207,7 +7207,7 @@
"type": "tidelift"
}
],
- "time": "2025-08-14T06:23:17+00:00"
+ "time": "2026-01-23T16:02:12+00:00"
},
{
"name": "symfony/routing",
@@ -7298,16 +7298,16 @@
},
{
"name": "symfony/service-contracts",
- "version": "v3.6.0",
+ "version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
+ "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
- "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
+ "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
"shasum": ""
},
"require": {
@@ -7361,7 +7361,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
},
"funding": [
{
@@ -7372,31 +7372,36 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-04-25T09:37:31+00:00"
+ "time": "2025-07-15T11:30:57+00:00"
},
{
"name": "symfony/string",
- "version": "v7.3.3",
+ "version": "v7.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
+ "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
- "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
+ "url": "https://api.github.com/repos/symfony/string/zipball/1c4b10461bf2ec27537b5f36105337262f5f5d6f",
+ "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f",
"shasum": ""
},
"require": {
"php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-grapheme": "~1.33",
"symfony/polyfill-intl-normalizer": "~1.0",
"symfony/polyfill-mbstring": "~1.0"
},
@@ -7404,12 +7409,11 @@
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
- "symfony/emoji": "^7.1",
- "symfony/error-handler": "^6.4|^7.0",
- "symfony/http-client": "^6.4|^7.0",
- "symfony/intl": "^6.4|^7.0",
+ "symfony/emoji": "^7.1|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
"symfony/translation-contracts": "^2.5|^3.0",
- "symfony/var-exporter": "^6.4|^7.0"
+ "symfony/var-exporter": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -7448,7 +7452,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v7.3.3"
+ "source": "https://github.com/symfony/string/tree/v7.4.4"
},
"funding": [
{
@@ -7468,7 +7472,7 @@
"type": "tidelift"
}
],
- "time": "2025-08-25T06:35:40+00:00"
+ "time": "2026-01-12T10:54:30+00:00"
},
{
"name": "symfony/translation",
@@ -7571,16 +7575,16 @@
},
{
"name": "symfony/translation-contracts",
- "version": "v3.6.0",
+ "version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation-contracts.git",
- "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d"
+ "reference": "65a8bc82080447fae78373aa10f8d13b38338977"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
- "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977",
+ "reference": "65a8bc82080447fae78373aa10f8d13b38338977",
"shasum": ""
},
"require": {
@@ -7629,7 +7633,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0"
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1"
},
"funding": [
{
@@ -7640,12 +7644,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2024-09-27T08:32:26+00:00"
+ "time": "2025-07-15T13:41:35+00:00"
},
{
"name": "symfony/uid",
@@ -8374,28 +8382,28 @@
},
{
"name": "webmozart/assert",
- "version": "1.11.0",
+ "version": "1.12.1",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
- "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
+ "reference": "9be6926d8b485f55b9229203f962b51ed377ba68"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
- "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68",
+ "reference": "9be6926d8b485f55b9229203f962b51ed377ba68",
"shasum": ""
},
"require": {
"ext-ctype": "*",
+ "ext-date": "*",
+ "ext-filter": "*",
"php": "^7.2 || ^8.0"
},
- "conflict": {
- "phpstan/phpstan": "<0.12.20",
- "vimeo/psalm": "<4.6.1 || 4.6.2"
- },
- "require-dev": {
- "phpunit/phpunit": "^8.5.13"
+ "suggest": {
+ "ext-intl": "",
+ "ext-simplexml": "",
+ "ext-spl": ""
},
"type": "library",
"extra": {
@@ -8426,9 +8434,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
- "source": "https://github.com/webmozarts/assert/tree/1.11.0"
+ "source": "https://github.com/webmozarts/assert/tree/1.12.1"
},
- "time": "2022-06-03T18:03:27+00:00"
+ "time": "2025-10-29T15:56:20+00:00"
},
{
"name": "wikimedia/composer-merge-plugin",
@@ -8488,18 +8496,111 @@
}
],
"packages-dev": [
+ {
+ "name": "brianium/paratest",
+ "version": "v7.4.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paratestphp/paratest.git",
+ "reference": "633c0987ecf6d9b057431225da37b088aa9274a5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paratestphp/paratest/zipball/633c0987ecf6d9b057431225da37b088aa9274a5",
+ "reference": "633c0987ecf6d9b057431225da37b088aa9274a5",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-simplexml": "*",
+ "fidry/cpu-core-counter": "^1.2.0",
+ "jean85/pretty-package-versions": "^2.0.6",
+ "php": "~8.2.0 || ~8.3.0 || ~8.4.0",
+ "phpunit/php-code-coverage": "^10.1.16",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-timer": "^6.0.0",
+ "phpunit/phpunit": "^10.5.47",
+ "sebastian/environment": "^6.1.0",
+ "symfony/console": "^6.4.7 || ^7.1.5",
+ "symfony/process": "^6.4.7 || ^7.1.5"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12.0.0",
+ "ext-pcov": "*",
+ "ext-posix": "*",
+ "phpstan/phpstan": "^1.12.6",
+ "phpstan/phpstan-deprecation-rules": "^1.2.1",
+ "phpstan/phpstan-phpunit": "^1.4.0",
+ "phpstan/phpstan-strict-rules": "^1.6.1",
+ "squizlabs/php_codesniffer": "^3.10.3",
+ "symfony/filesystem": "^6.4.3 || ^7.1.5"
+ },
+ "bin": [
+ "bin/paratest",
+ "bin/paratest_for_phpstorm"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ParaTest\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Scaturro",
+ "email": "scaturrob@gmail.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Filippo Tessarotto",
+ "email": "zoeslam@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "Parallel testing for PHP",
+ "homepage": "https://github.com/paratestphp/paratest",
+ "keywords": [
+ "concurrent",
+ "parallel",
+ "phpunit",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/paratestphp/paratest/issues",
+ "source": "https://github.com/paratestphp/paratest/tree/v7.4.9"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/Slamdunk",
+ "type": "github"
+ },
+ {
+ "url": "https://paypal.me/filippotessarotto",
+ "type": "paypal"
+ }
+ ],
+ "time": "2025-06-25T06:09:59+00:00"
+ },
{
"name": "composer/semver",
- "version": "3.4.3",
+ "version": "3.4.4",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
- "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12"
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
- "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
+ "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
"shasum": ""
},
"require": {
@@ -8551,7 +8652,7 @@
"support": {
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues",
- "source": "https://github.com/composer/semver/tree/3.4.3"
+ "source": "https://github.com/composer/semver/tree/3.4.4"
},
"funding": [
{
@@ -8561,13 +8662,9 @@
{
"url": "https://github.com/composer",
"type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/composer/composer",
- "type": "tidelift"
}
],
- "time": "2024-09-19T14:15:21+00:00"
+ "time": "2025-08-20T19:15:30+00:00"
},
{
"name": "doctrine/cache",
@@ -8775,29 +8872,29 @@
},
{
"name": "doctrine/deprecations",
- "version": "1.1.5",
+ "version": "1.1.6",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
- "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38"
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
- "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"conflict": {
- "phpunit/phpunit": "<=7.5 || >=13"
+ "phpunit/phpunit": "<=7.5 || >=14"
},
"require-dev": {
- "doctrine/coding-standard": "^9 || ^12 || ^13",
- "phpstan/phpstan": "1.4.10 || 2.1.11",
+ "doctrine/coding-standard": "^9 || ^12 || ^14",
+ "phpstan/phpstan": "1.4.10 || 2.1.30",
"phpstan/phpstan-phpunit": "^1.0 || ^2",
- "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0",
"psr/log": "^1 || ^2 || ^3"
},
"suggest": {
@@ -8817,9 +8914,9 @@
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
- "source": "https://github.com/doctrine/deprecations/tree/1.1.5"
+ "source": "https://github.com/doctrine/deprecations/tree/1.1.6"
},
- "time": "2025-04-07T20:06:18+00:00"
+ "time": "2026-02-07T07:09:04+00:00"
},
{
"name": "doctrine/event-manager",
@@ -8975,6 +9072,67 @@
},
"time": "2024-11-21T13:46:39+00:00"
},
+ {
+ "name": "fidry/cpu-core-counter",
+ "version": "1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theofidry/cpu-core-counter.git",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "fidry/makefile": "^0.2.0",
+ "fidry/php-cs-fixer-config": "^1.1.2",
+ "phpstan/extension-installer": "^1.2.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-deprecation-rules": "^2.0.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^8.5.31 || ^9.5.26",
+ "webmozarts/strict-phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Fidry\\CpuCoreCounter\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Théo FIDRY",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Tiny utility to get the number of CPU cores.",
+ "keywords": [
+ "CPU",
+ "core"
+ ],
+ "support": {
+ "issues": "https://github.com/theofidry/cpu-core-counter/issues",
+ "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theofidry",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-14T07:29:31+00:00"
+ },
{
"name": "filp/whoops",
"version": "2.18.0",
@@ -9138,6 +9296,66 @@
},
"time": "2024-03-22T22:46:32+00:00"
},
+ {
+ "name": "jean85/pretty-package-versions",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Jean85/pretty-package-versions.git",
+ "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
+ "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.1.0",
+ "php": "^7.4|^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "jean85/composer-provided-replaced-stub-package": "^1.0",
+ "phpstan/phpstan": "^2.0",
+ "phpunit/phpunit": "^7.5|^8.5|^9.6",
+ "rector/rector": "^2.0",
+ "vimeo/psalm": "^4.3 || ^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Jean85\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Alessandro Lai",
+ "email": "alessandro.lai85@gmail.com"
+ }
+ ],
+ "description": "A library to get pretty versions strings of installed dependencies",
+ "keywords": [
+ "composer",
+ "package",
+ "release",
+ "versions"
+ ],
+ "support": {
+ "issues": "https://github.com/Jean85/pretty-package-versions/issues",
+ "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
+ },
+ "time": "2025-03-19T14:43:43+00:00"
+ },
{
"name": "larastan/larastan",
"version": "v2.11.0",
@@ -9510,16 +9728,16 @@
},
{
"name": "myclabs/deep-copy",
- "version": "1.13.1",
+ "version": "1.13.4",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
- "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"shasum": ""
},
"require": {
@@ -9558,7 +9776,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
},
"funding": [
{
@@ -9566,20 +9784,20 @@
"type": "tidelift"
}
],
- "time": "2025-04-29T12:36:36+00:00"
+ "time": "2025-08-01T08:46:24+00:00"
},
{
"name": "nikic/php-parser",
- "version": "v5.4.0",
+ "version": "v5.7.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494"
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
"shasum": ""
},
"require": {
@@ -9598,7 +9816,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "5.0-dev"
+ "dev-master": "5.x-dev"
}
},
"autoload": {
@@ -9622,9 +9840,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
},
- "time": "2024-12-30T11:07:19+00:00"
+ "time": "2025-12-06T11:56:16+00:00"
},
{
"name": "nunomaduro/collision",
@@ -10637,16 +10855,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "10.5.46",
+ "version": "10.5.63",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d"
+ "reference": "33198268dad71e926626b618f3ec3966661e4d90"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d",
- "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90",
+ "reference": "33198268dad71e926626b618f3ec3966661e4d90",
"shasum": ""
},
"require": {
@@ -10656,7 +10874,7 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.13.1",
+ "myclabs/deep-copy": "^1.13.4",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.1",
@@ -10667,13 +10885,13 @@
"phpunit/php-timer": "^6.0.0",
"sebastian/cli-parser": "^2.0.1",
"sebastian/code-unit": "^2.0.0",
- "sebastian/comparator": "^5.0.3",
+ "sebastian/comparator": "^5.0.5",
"sebastian/diff": "^5.1.1",
"sebastian/environment": "^6.1.0",
- "sebastian/exporter": "^5.1.2",
+ "sebastian/exporter": "^5.1.4",
"sebastian/global-state": "^6.0.2",
"sebastian/object-enumerator": "^5.0.0",
- "sebastian/recursion-context": "^5.0.0",
+ "sebastian/recursion-context": "^5.0.1",
"sebastian/type": "^4.0.0",
"sebastian/version": "^4.0.1"
},
@@ -10718,7 +10936,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63"
},
"funding": [
{
@@ -10742,7 +10960,7 @@
"type": "tidelift"
}
],
- "time": "2025-05-02T06:46:24+00:00"
+ "time": "2026-01-27T05:48:37+00:00"
},
{
"name": "psr/cache",
@@ -11042,16 +11260,16 @@
},
{
"name": "sebastian/comparator",
- "version": "5.0.3",
+ "version": "5.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e"
+ "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
- "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d",
+ "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d",
"shasum": ""
},
"require": {
@@ -11107,15 +11325,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
- "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3"
+ "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
}
],
- "time": "2024-10-18T14:56:07+00:00"
+ "time": "2026-01-24T09:25:16+00:00"
},
{
"name": "sebastian/complexity",
@@ -11308,16 +11538,16 @@
},
{
"name": "sebastian/exporter",
- "version": "5.1.2",
+ "version": "5.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "955288482d97c19a372d3f31006ab3f37da47adf"
+ "reference": "0735b90f4da94969541dac1da743446e276defa6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
- "reference": "955288482d97c19a372d3f31006ab3f37da47adf",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6",
+ "reference": "0735b90f4da94969541dac1da743446e276defa6",
"shasum": ""
},
"require": {
@@ -11326,7 +11556,7 @@
"sebastian/recursion-context": "^5.0"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
@@ -11374,15 +11604,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
- "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
}
],
- "time": "2024-03-02T07:17:12+00:00"
+ "time": "2025-09-24T06:09:11+00:00"
},
{
"name": "sebastian/global-state",
@@ -11618,23 +11860,23 @@
},
{
"name": "sebastian/recursion-context",
- "version": "5.0.0",
+ "version": "5.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "05909fb5bc7df4c52992396d0116aed689f93712"
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712",
- "reference": "05909fb5bc7df4c52992396d0116aed689f93712",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a",
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
@@ -11669,15 +11911,28 @@
"homepage": "https://github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
- "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0"
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
}
],
- "time": "2023-02-03T07:05:40+00:00"
+ "time": "2025-08-10T07:50:56+00:00"
},
{
"name": "sebastian/type",
@@ -11862,16 +12117,16 @@
},
{
"name": "theseer/tokenizer",
- "version": "1.2.3",
+ "version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
- "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
- "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
"shasum": ""
},
"require": {
@@ -11900,7 +12155,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+ "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
},
"funding": [
{
@@ -11908,7 +12163,7 @@
"type": "github"
}
],
- "time": "2024-03-03T12:36:25+00:00"
+ "time": "2025-11-17T20:03:58+00:00"
}
],
"aliases": [],
@@ -11920,5 +12175,5 @@
"php": ">=8.1"
},
"platform-dev": {},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}
diff --git a/config/config.php b/config/config.php
index 124dbe6cc..ec7461328 100755
--- a/config/config.php
+++ b/config/config.php
@@ -29,16 +29,23 @@
// 'vendor_components_resource_path' => 'assets/vendor/js/components',
'enabled_currencies' => explode(',', env('MODULARITY_ACTIVE_CURRENCIES', 'USD,EUR,TRY')),
+ /**
+ * Optional custom currency provider class implementing CurrencyProviderInterface.
+ * When null, SystemPricingCurrencyProvider is used if available, else NullCurrencyProvider.
+ */
+ 'currency_provider' => env('MODULARITY_CURRENCY_PROVIDER', null),
+
'manifest' => 'unusual-manifest.json',
'js_namespace' => env('VUE_APP_NAME', 'MODULARITY'),
'build_timeout' => 300,
'use_big_integers_on_migrations' => true,
'use_collation_for_search' => env('MODULARITY_USE_COLLATION_FOR_SEARCH', false),
- 'use_inertia' => env('MODULARITY_USE_INERTIA', false),
+ 'use_inertia' => env('MODULARITY_USE_INERTIA', true),
'include_transaction_fee' => env('MODULARITY_INCLUDE_TRANSACTION_FEE', false),
'use_country_based_vat_rates' => env('MODULARITY_USE_COUNTRY_BASED_VAT_RATES', false),
'use_language_based_prices' => env('MODULARITY_USE_LANGUAGE_BASED_PRICES', false),
+ 'use_format_item_eager' => env('MODULARITY_USE_FORMAT_ITEM_EAGER', false),
'language_currencies' => [],
'hide_description_for_language_based_prices' => env('MODULARITY_HIDE_DESCRIPTION_FOR_LANGUAGE_BASED_PRICES', false),
'disable_billing_banner' => env('MODULARITY_DISABLE_BILLING_BANNER', false),
diff --git a/config/defers/auth_component.php b/config/defers/auth_component.php
new file mode 100644
index 000000000..82dc3401c
--- /dev/null
+++ b/config/defers/auth_component.php
@@ -0,0 +1,41 @@
+ false,
+
+ 'formWidth' => [
+ 'xs' => '85vw',
+ 'sm' => '450px',
+ 'md' => '450px',
+ 'lg' => '500px',
+ 'xl' => '600px',
+ 'xxl' => 700,
+ ],
+
+ 'layout' => [
+ 'leftColumnClass' => 'py-12 d-flex flex-column align-center justify-center bg-white',
+ 'rightColumnClass' => 'px-xs-12 py-xs-3 px-sm-12 py-sm-3 pa-12 pa-md-0 d-flex flex-column align-center justify-center col-right bg-primary',
+ 'bannerMaxWidth' => '420px',
+ ],
+
+ 'banner' => [
+ 'titleClass' => 'text-white mt-5 text-h4 custom-mb-8rem fs-2rem',
+ 'buttonVariant' => 'outlined',
+ 'buttonClass' => 'text-white custom-right-auth-button my-5',
+ ],
+
+ 'dividerText' => 'or',
+];
diff --git a/config/defers/auth_pages.php b/config/defers/auth_pages.php
new file mode 100644
index 000000000..e2e4d757c
--- /dev/null
+++ b/config/defers/auth_pages.php
@@ -0,0 +1,132 @@
+ 'ue-auth',
+ /*
+ |--------------------------------------------------------------------------
+ | Default layout attributes (ue-auth component)
+ |--------------------------------------------------------------------------
+ */
+ 'layout' => [
+ 'logoSymbol' => 'main-logo-dark',
+ 'logoLightSymbol' => 'main-logo-light',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Auth page definitions
+ |--------------------------------------------------------------------------
+ | Each key maps to a controller method. Override formDraft, actionRoute,
+ | buttonText, layoutPreset, formSlotsPreset, slotsPreset to customize.
+ | Use 'attributes' to pass page-specific props to the auth component (ue-auth or ue-custom-auth).
+ */
+ 'pages' => [
+ 'login' => [
+ 'pageTitle' => 'authentication.login',
+ 'layoutPreset' => 'banner',
+ 'formDraft' => 'login_form',
+ 'actionRoute' => 'admin.login',
+ 'formTitle' => 'authentication.login-title',
+ 'buttonText' => 'authentication.sign-in',
+ 'formSlotsPreset' => 'login_options',
+ 'slotsPreset' => 'login_bottom',
+ ],
+ 'register' => [
+ 'pageTitle' => 'authentication.register',
+ 'layoutPreset' => 'banner',
+ 'formDraft' => 'register_form',
+ 'actionRoute' => 'admin.register',
+ 'formTitle' => 'authentication.create-an-account',
+ 'buttonText' => 'authentication.register',
+ 'formSlotsPreset' => 'have_account',
+ 'slotsPreset' => 'register_bottom',
+ ],
+ 'pre_register' => [
+ 'pageTitle' => 'authentication.register',
+ 'layoutPreset' => 'banner',
+ 'formDraft' => 'pre_register_form',
+ 'actionRoute' => 'admin.register.verification',
+ 'formTitle' => 'authentication.create-an-account',
+ 'buttonText' => 'authentication.register',
+ 'formSlotsPreset' => 'have_account',
+ 'slotsPreset' => 'register_bottom',
+ ],
+ 'complete_register' => [
+ 'pageTitle' => 'authentication.complete-registration',
+ 'layoutPreset' => 'minimal',
+ 'formDraft' => 'complete_register_form',
+ 'actionRoute' => 'admin.complete.register',
+ 'formTitle' => 'authentication.complete-registration',
+ 'buttonText' => 'Complete',
+ 'formSlotsPreset' => 'restart',
+ 'slotsPreset' => null,
+ ],
+ 'forgot_password' => [
+ 'pageTitle' => 'authentication.forgot-password',
+ 'layoutPreset' => 'minimal',
+ 'formDraft' => 'forgot_password_form',
+ 'actionRoute' => 'admin.password.reset.email',
+ 'formTitle' => 'authentication.forgot-password',
+ 'buttonText' => 'authentication.reset-send',
+ 'formOverrides' => ['hasSubmit' => false],
+ 'formSlotsPreset' => 'forgot_password_form',
+ 'slotsPreset' => 'forgot_password_bottom',
+ ],
+ 'reset_password' => [
+ 'pageTitle' => 'authentication.reset-password',
+ 'layoutPreset' => 'minimal',
+ 'formDraft' => 'reset_password_form',
+ 'actionRoute' => 'admin.password.reset.update',
+ 'formTitle' => 'authentication.reset-password',
+ 'buttonText' => 'authentication.reset-password',
+ 'formOverrides' => ['hasSubmit' => true, 'color' => 'primary', 'formClass' => 'px-5'],
+ 'formSlotsPreset' => 'resend',
+ 'slotsPreset' => null,
+ ],
+ 'oauth_password' => [
+ 'pageTitle' => 'authentication.confirm-provider',
+ 'layoutPreset' => 'minimal_no_divider',
+ 'formDraft' => null,
+ 'actionRoute' => 'admin.login.oauth.linkProvider',
+ 'formTitle' => 'authentication.confirm-provider',
+ 'buttonText' => 'authentication.sign-in',
+ 'formSlotsPreset' => 'oauth_submit',
+ 'slotsPreset' => null,
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Layout presets (structural flags only)
+ |--------------------------------------------------------------------------
+ | Content attributes (bannerDescription, bannerSubDescription, redirectUrl, etc.)
+ | come from app config: modularity/auth_pages.php attributes and pages.[page].attributes
+ */
+ 'layoutPresets' => [
+ 'banner' => [
+ 'noSecondSection' => false,
+ ],
+ 'minimal' => [
+ 'noSecondSection' => true,
+ ],
+ 'minimal_no_divider' => [
+ 'noSecondSection' => false,
+ 'noDivider' => true,
+ ],
+ ],
+];
diff --git a/config/defers/form_drafts.php b/config/defers/form_drafts.php
index ce97434c6..ebefca717 100755
--- a/config/defers/form_drafts.php
+++ b/config/defers/form_drafts.php
@@ -278,9 +278,9 @@
],
],
'login_form' => [
- 'timezone' => [
- 'type' => '_timezone',
- ],
+ // 'timezone' => [
+ // 'type' => '_timezone',
+ // ],
'email' => [
'type' => 'text',
'name' => 'email',
diff --git a/config/defers/ui_settings.php b/config/defers/ui_settings.php
index 5a7d54ddb..4e7e1992c 100644
--- a/config/defers/ui_settings.php
+++ b/config/defers/ui_settings.php
@@ -6,9 +6,10 @@
],
'sidebar' => [
'width' => 264,
+ 'expandHover' => 'hidden', // 'mini' | 'hidden'
'expandOnHover' => true,
'rail' => false,
- 'location' => 'left',
+ 'location' => 'left', // 'left' | 'right'
'persistent' => false,
'hideIcons' => false,
'railWidth' => 130,
@@ -29,6 +30,26 @@
'permanent' => true,
'max-width' => '10em',
],
+ /**
+ * Top bar (v-app-bar) configuration.
+ * User preferences override these defaults and are persisted in DB.
+ */
+ 'topbar' => [
+ 'enabled' => true,
+ 'fixed' => false,
+ 'order' => 0, // Vuetify layout order (0 = above drawer)
+ 'showOnMobile' => true,
+ 'showOnDesktop' => false,
+ ],
+ /**
+ * Bottom navigation (v-bottom-navigation) configuration.
+ * Useful for mobile-first layouts.
+ */
+ 'bottomNavigation' => [
+ 'enabled' => false,
+ 'showOnMobile' => true,
+ 'showOnDesktop' => false,
+ ],
'dashboard' => [
'blocks' => [
diff --git a/config/merges/api.php b/config/merges/api.php
index 1fa8e02ce..8dcc1d7a5 100644
--- a/config/merges/api.php
+++ b/config/merges/api.php
@@ -13,6 +13,7 @@
'auth_middlewares' => [
'auth:sanctum',
],
+ 'routes' => [], // Additional API resource routes to merge with default (index, store, show, update, destroy)
'versioning' => [
'enabled' => true,
'default_version' => 'v1',
diff --git a/config/merges/default_table_attributes.php b/config/merges/default_table_attributes.php
index e979f5b98..68086449b 100644
--- a/config/merges/default_table_attributes.php
+++ b/config/merges/default_table_attributes.php
@@ -140,6 +140,7 @@
'headerOptions' => [
'color' => 'rgba(140,160,167, .2)', // Hex, rgba or default css colors
],
+ 'fixedLastColumn' => true,
'formAttributes' => [
'rowAttribute' => [
diff --git a/database/migrations/default/2022_01_22_000002_create_modularity_companies_table.php b/database/migrations/default/2022_01_22_000002_create_modularity_companies_table.php
index aaf78c6ee..5ea51c772 100755
--- a/database/migrations/default/2022_01_22_000002_create_modularity_companies_table.php
+++ b/database/migrations/default/2022_01_22_000002_create_modularity_companies_table.php
@@ -12,10 +12,10 @@ public function up()
// this will create an id, a "published" column, and soft delete and timestamps columns
createDefaultTableFields($table);
// $table->{modularityIntegerMethod()}("_id")->unsigned();
- $table->string('name', 30)->nullable();
+ $table->string('name', 99)->nullable();
$table->text('address')->nullable();
- $table->string('city', 30)->nullable();
- $table->string('state', 30)->nullable();
+ $table->string('city', 50)->nullable();
+ $table->string('state', 50)->nullable();
$table->integer('country_id')->nullable();
$table->string('zip_code', 10)->nullable();
$table->string('phone', 20)->nullable();
diff --git a/database/migrations/default/2024_12_22_161730_create_modularity_chats_table.php b/database/migrations/default/2024_12_22_161730_create_modularity_chats_table.php
index 0d4ded43c..bfd2b2da4 100644
--- a/database/migrations/default/2024_12_22_161730_create_modularity_chats_table.php
+++ b/database/migrations/default/2024_12_22_161730_create_modularity_chats_table.php
@@ -31,7 +31,7 @@ public function up()
->onDelete('cascade')
->onUpdate('cascade');
- $table->text('content');
+ $table->text('content')->nullable();
$table->boolean('is_read')->default(false);
$table->boolean('is_starred')->default(false);
$table->boolean('is_pinned')->default(false);
diff --git a/database/migrations/default/2025_04_03_125319_create_user_oauths_table.php b/database/migrations/default/2025_04_03_125319_create_user_oauths_table.php
index 1df82a66c..4175b39db 100644
--- a/database/migrations/default/2025_04_03_125319_create_user_oauths_table.php
+++ b/database/migrations/default/2025_04_03_125319_create_user_oauths_table.php
@@ -17,7 +17,7 @@ public function up(): void
Schema::create($userOauthTable, function (Blueprint $table) use ($usersTable) {
$table->bigIncrements('id');
$table->timestamps();
- $table->string('token')->index();
+ $table->text('token')->index();
$table->string('provider')->index();
$table->longText('avatar')->nullable();
$table->string('oauth_id')->index();
diff --git a/database/migrations/default/2025_08_14_124257_update_content_field_of_chat_messages_table.php b/database/migrations/default/2025_08_14_124257_update_content_field_of_chat_messages_table.php
deleted file mode 100644
index f6a9cf2b8..000000000
--- a/database/migrations/default/2025_08_14_124257_update_content_field_of_chat_messages_table.php
+++ /dev/null
@@ -1,30 +0,0 @@
-text('content')->nullable()->change();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- $chatMessagesTable = modularityConfig('tables.chat_messages', 'um_chat_messages');
- Schema::table($chatMessagesTable, function (Blueprint $table) {
- $table->text('content')->nullable(false)->change();
- });
- }
-};
diff --git a/database/migrations/default/2025_08_20_124257_update_token_field_of_user_oauths_table.php b/database/migrations/default/2025_08_20_124257_update_token_field_of_user_oauths_table.php
deleted file mode 100644
index f51c85f52..000000000
--- a/database/migrations/default/2025_08_20_124257_update_token_field_of_user_oauths_table.php
+++ /dev/null
@@ -1,31 +0,0 @@
-text('token')->change();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- $userOauthTable = modularityConfig('tables.user_oauths', 'um_user_oauths');
- Schema::table($userOauthTable, function (Blueprint $table) {
- $table->string('token')->nullable(false)->change();
- });
- }
-};
diff --git a/database/migrations/default/2025_09_05_133344_update_companies_name_field_length.php b/database/migrations/default/2025_09_05_133344_update_companies_name_field_length.php
deleted file mode 100644
index 9333a2a78..000000000
--- a/database/migrations/default/2025_09_05_133344_update_companies_name_field_length.php
+++ /dev/null
@@ -1,29 +0,0 @@
-string('name', 99)->nullable()->change();
- $table->string('city', 50)->nullable()->change();
- $table->string('state', 50)->nullable()->change();
- });
- }
-
- public function down()
- {
- Schema::table(modularityConfig('tables.companies', 'um_companies'), function (Blueprint $table) {
- $table->string('name', 30)->nullable()->change();
- $table->string('city', 30)->nullable()->change();
- $table->string('state', 30)->nullable()->change();
- });
- }
-};
diff --git a/database/migrations/default/2026_02_23_120000_add_ui_preferences_to_users_table.php b/database/migrations/default/2026_02_23_120000_add_ui_preferences_to_users_table.php
new file mode 100644
index 000000000..4437ccfc2
--- /dev/null
+++ b/database/migrations/default/2026_02_23_120000_add_ui_preferences_to_users_table.php
@@ -0,0 +1,37 @@
+json('ui_preferences')->nullable()->after('timezone');
+ });
+ }
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ $usersTable = modularityConfig('tables.users', 'um_users');
+
+ if (Schema::hasTable($usersTable) && Schema::hasColumn($usersTable, 'ui_preferences')) {
+ Schema::table($usersTable, function (Blueprint $table) {
+ $table->dropColumn('ui_preferences');
+ });
+ }
+ }
+};
diff --git a/docs/CONSOLE_CONVENTIONS.md b/docs/CONSOLE_CONVENTIONS.md
new file mode 100644
index 000000000..f4ac89f69
--- /dev/null
+++ b/docs/CONSOLE_CONVENTIONS.md
@@ -0,0 +1,72 @@
+# Console Command Naming Conventions
+
+## Rule: Class Name ↔ Signature Compatibility
+
+**Class names must reflect their command signature.** Convert signature parts to PascalCase and append `Command`.
+
+| Signature Part | Class Name Part | Example |
+|----------------|-----------------|---------|
+| `modularity:make:module` | MakeModuleCommand | make + module |
+| `modularity:cache:clear` | CacheClearCommand | cache + clear |
+| `modularity:route:disable` | RouteDisableCommand | route + disable |
+| `modularity:replace:regex` | ReplaceRegexCommand | replace + regex |
+
+## Semantic Rules
+
+### `modularity:make:*` — Artifact generators
+Commands that **scaffold or generate files**. All live in `Console/Make/`.
+
+- **Class:** `Make*Command` (e.g. `MakeModuleCommand`, `MakeControllerCommand`)
+- **Examples:** `make:module`, `make:controller`, `make:migration`
+
+### `modularity:create:*` — Runtime creation
+Commands that **create runtime records** (DB entries, users).
+
+- **Class:** `Create*Command` (e.g. `CreateSuperAdminCommand`)
+- **Examples:** `create:superadmin`
+
+### Other namespaces
+- `modularity:cache:*` → `Cache*Command` (CacheClearCommand, CacheListCommand)
+- `modularity:migrate:*` → `Migrate*Command` (MigrateCommand, MigrateRefreshCommand)
+- `modularity:flush:*` → `Flush*Command` (FlushCommand, FlushSessionsCommand)
+- `modularity:route:*` → `Route*Command` (RouteDisableCommand, RouteEnableCommand)
+- `modularity:sync:*` → `Sync*Command` (SyncTranslationsCommand, SyncStatesCommand)
+- `modularity:replace:*` → `Replace*Command` (ReplaceRegexCommand)
+
+## Class Naming Pattern by Folder
+
+| Folder | Pattern | Example |
+|-----------|--------------------|-----------------------------|
+| Make/ | `Make*Command` | MakeModuleCommand |
+| Setup/ | `*Command` | CreateSuperAdminCommand, InstallCommand |
+| Cache/ | `Cache*Command` | CacheClearCommand |
+| Migration/| `Migrate*Command` | MigrateCommand |
+| Flush/ | `Flush*Command` | FlushCommand, FlushSessionsCommand |
+| Module/ | `*Command` | RouteDisableCommand, FixModuleCommand |
+| Sync/ | `Sync*Command` | SyncTranslationsCommand |
+| Docs/ | `Generate*Command` | GenerateCommandDocsCommand |
+
+## Full Command Mapping (Signature ↔ Class)
+
+| Signature | Class |
+|-----------|-------|
+| modularity:make:* | Make*Command |
+| modularity:create:superadmin | CreateSuperAdminCommand |
+| modularity:create:database | CreateDatabaseCommand |
+| modularity:install | InstallCommand |
+| modularity:setup:development | SetupModularityDevelopmentCommand |
+| modularity:cache:list | CacheListCommand |
+| modularity:cache:clear | CacheClearCommand |
+| modularity:cache:versions | CacheVersionsCommand |
+| modularity:cache:graph | CacheGraphCommand |
+| modularity:cache:stats | CacheStatsCommand |
+| modularity:cache:warm | CacheWarmCommand |
+| modularity:flush | FlushCommand |
+| modularity:flush:sessions | FlushSessionsCommand |
+| modularity:flush:filepond | FlushFilepondCommand |
+| modularity:route:disable | RouteDisableCommand |
+| modularity:route:enable | RouteEnableCommand |
+| modularity:fix:module | FixModuleCommand |
+| modularity:remove:module | RemoveModuleCommand |
+| modularity:replace:regex | ReplaceRegexCommand |
+| modularity:db:check-collation | CheckDatabaseCollationCommand |
diff --git a/docs/build/404.html b/docs/build/404.html
index 66925e3b0..4980b2e3d 100644
--- a/docs/build/404.html
+++ b/docs/build/404.html
@@ -8,14 +8,14 @@
-
+
-
+