diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 02baa388..85626ba4 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -34,7 +34,7 @@ jobs: # Login against a Docker registry except on PR # https://github.com/docker/login-action - name: Log into registry ${{ env.REGISTRY }} - if: ${{ github.event_name != 'pull_request' }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} uses: docker/login-action@v3.1.0 with: registry: ${{ env.REGISTRY }} @@ -48,6 +48,14 @@ jobs: uses: docker/metadata-action@v5.5.1 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action @@ -57,7 +65,7 @@ jobs: with: context: . platforms: linux/amd64,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} + push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index 0e686c42..00000000 --- a/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -nodejs 18.20.4 diff --git a/Makefile b/Makefile deleted file mode 100644 index c6e22819..00000000 --- a/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -SHELL=/bin/bash -baseDir := $(shell pwd) -userRights := $(shell id -u):$(shell id -g) - -up: - docker run --rm -u "$(userRights)" -v "$(baseDir):/var/www/html" -w "/var/www/html" laravelsail/php81-composer:latest composer install --ignore-platform-reqs - ./vendor/bin/sail up -d - npm ci - @echo "Run dev build of assets" - npm run dev - ./vendor/bin/sail artisan migrate - ./vendor/bin/sail artisan key:generate - @echo "Application is up and running http://localhost:8500" diff --git a/README.md b/README.md index 97e4188c..8e8b18fd 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ We are using [Laravel Sail](https://laravel.com/docs/master/sail) to develop Inp - [Docker](https://www.docker.com/get-started/) - [NodeJS](https://nodejs.org/) (v16 LTS preferred) +- [mise-en-place](https://mise.jdx.dev/) (for task automation) ### Download @@ -39,10 +40,10 @@ cp .env.dev.example .env ### Running -Make sure that your Docker agent is running. There are several steps necessary to build the app for the first time. To simplify these tasks, we have a Makefile in place. Just run the following command, and all build steps will run automatically: +Make sure that your Docker agent is running. There are several steps necessary to build the app for the first time. To simplify these tasks, we use mise-en-place. Just run the following command, and all build steps will run automatically: ```bash -make up +mise task up ``` ### Webpack (Mix) diff --git a/app/Http/Controllers/Api/FormBlockLogicController.php b/app/Http/Controllers/Api/FormBlockLogicController.php new file mode 100644 index 00000000..4bdfb889 --- /dev/null +++ b/app/Http/Controllers/Api/FormBlockLogicController.php @@ -0,0 +1,28 @@ +json($block->formBlockLogics()->create($request->validated()), 201); + } + + public function update(FormBlockLogicRequest $request, FormBlockLogic $logic) + { + return response()->json($logic->update($request->validated())); + } + + public function delete(FormBlockLogic $logic) + { + $logic->delete(); + + return response()->json(null, 200); + } +} diff --git a/app/Http/Requests/FormBlockLogicRequest.php b/app/Http/Requests/FormBlockLogicRequest.php new file mode 100644 index 00000000..98b5b59e --- /dev/null +++ b/app/Http/Requests/FormBlockLogicRequest.php @@ -0,0 +1,50 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => 'required|string|max:255', + 'conditions' => 'required|array', + 'conditions.*.source' => 'required|string', + 'conditions.*.operator' => 'required|string|in:equals,equalsNot,contains,containsNot,isLowerThan,isGreaterThan', + 'conditions.*.value' => 'required|string', + 'conditions.*.chainOperator' => 'required|string|in:and,or', + 'action' => 'required|string|in:hide,show,goto', + 'action_payload' => 'nullable|string', + 'evaluate' => 'required|string|in:before,after', + ]; + } + + public function messages() + { + return [ + 'name.required' => 'The name is required.', + 'conditions.required' => 'At least one condition is required.', + 'conditions.*.source.required' => 'The source block for your #:position condition is required.', + 'conditions.*.value.required' => 'The value for your #:position condition is required.', + 'conditions.*.operator.required' => 'The operator for the #:position condition is required.', + 'conditions.*.chainOperator.required' => 'The chain operator for the #:position condition is required.', + 'action.required' => 'The action is required.', + 'evaluate.required' => 'The evaluate is required.', + ]; + } +} diff --git a/app/Http/Resources/PublicFormBlockLogicResource.php b/app/Http/Resources/PublicFormBlockLogicResource.php new file mode 100644 index 00000000..92f97b17 --- /dev/null +++ b/app/Http/Resources/PublicFormBlockLogicResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->uuid, + 'name' => $this->name, + 'action' => $this->action, + 'evaluate' => $this->evaluate, + 'conditions' => $this->conditions, + 'action_payload' => $this->action_payload, + ]; + } +} diff --git a/app/Http/Resources/PublicFormBlockResource.php b/app/Http/Resources/PublicFormBlockResource.php index 1bfc0571..2ea2a7c6 100644 --- a/app/Http/Resources/PublicFormBlockResource.php +++ b/app/Http/Resources/PublicFormBlockResource.php @@ -3,6 +3,7 @@ namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; +use App\Http\Resources\PublicFormBlockLogicResource; class PublicFormBlockResource extends JsonResource { @@ -14,6 +15,7 @@ class PublicFormBlockResource extends JsonResource */ public function toArray($request) { + $logics = $this->formBlockLogics; $interactions = $this->activeInteractions; if ($this->options && $this->options['randomize_responses'] === true) { @@ -34,6 +36,7 @@ public function toArray($request) 'is_required' => $this->is_required, 'parent_block' => $this->parent_block, 'interactions' => PublicFormBlockInteractionResource::collection($interactions), + 'logics' => PublicFormBlockLogicResource::collection($logics), ]; } } diff --git a/app/Models/FormBlock.php b/app/Models/FormBlock.php index 6d3968ce..27b49ae8 100644 --- a/app/Models/FormBlock.php +++ b/app/Models/FormBlock.php @@ -2,12 +2,13 @@ namespace App\Models; -use App\Enums\FormBlockInteractionType; -use App\Enums\FormBlockType; -use App\Scopes\Sequence; use Hashids\Hashids; -use Illuminate\Database\Eloquent\Factories\HasFactory; +use App\Scopes\Sequence; use Webpatser\Uuid\Uuid; +use App\Enums\FormBlockType; +use App\Models\FormBlockLogic; +use App\Enums\FormBlockInteractionType; +use Illuminate\Database\Eloquent\Factories\HasFactory; class FormBlock extends BaseModel { @@ -26,7 +27,7 @@ class FormBlock extends BaseModel protected $guarded = []; - protected $with = ['formBlockInteractions']; + protected $with = ['formBlockInteractions', 'formBlockLogics']; protected $casts = [ 'is_required' => 'boolean', @@ -38,10 +39,12 @@ class FormBlock extends BaseModel protected $appends = [ 'interactions', + 'logics', ]; protected $hidden = [ 'formBlockInteractions', + 'formBlockLogics', ]; protected static function boot() @@ -93,6 +96,11 @@ public function formBlockInteractions() return $this->hasMany(FormBlockInteraction::class, 'form_block_id'); } + public function formBlockLogics() + { + return $this->hasMany(FormBlockLogic::class, 'form_block_id'); + } + public function activeInteractions() { return $this->hasMany(FormBlockInteraction::class, 'form_block_id') @@ -106,6 +114,11 @@ public function getInteractionsAttribute() return $this->formBlockInteractions; } + public function getLogicsAttribute() + { + return $this->formBlockLogics; + } + public function getSessionCountAttribute() { return $this->formSessionResponses()->selectRaw('COUNT(DISTINCT form_session_id) as count')->first()->count; diff --git a/app/Models/FormBlockLogic.php b/app/Models/FormBlockLogic.php new file mode 100644 index 00000000..e0f67e33 --- /dev/null +++ b/app/Models/FormBlockLogic.php @@ -0,0 +1,33 @@ + 'array', + 'action_payload' => 'array', + ]; + + protected static function boot() + { + parent::boot(); + + self::creating(function ($model) { + $model->uuid = (string) Str::orderedUuid(); + }); + } + + public function formBlock() + { + return $this->belongsTo(FormBlock::class); + } +} diff --git a/database/factories/FormBlockLogicFactory.php b/database/factories/FormBlockLogicFactory.php new file mode 100644 index 00000000..1747367e --- /dev/null +++ b/database/factories/FormBlockLogicFactory.php @@ -0,0 +1,36 @@ + + */ +class FormBlockLogicFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name(), + 'action' => 'hide', + 'evaluate' => 'before', + 'conditions' => [ + [ + 'source' => $this->faker->uuid, + 'operator' => 'equals', + 'value' => 'test', + 'chainOperator' => 'and', + ] + ], + 'action_payload' => [], + 'form_block_id' => FormBlock::factory(), + ]; + } +} diff --git a/database/migrations/2024_10_04_132306_create_form_block_rules_table.php b/database/migrations/2024_10_04_132306_create_form_block_rules_table.php new file mode 100644 index 00000000..42417513 --- /dev/null +++ b/database/migrations/2024_10_04_132306_create_form_block_rules_table.php @@ -0,0 +1,25 @@ +bigIncrements('id'); + $table->char('uuid', 36); + $table->string('name'); + $table->json('conditions'); + $table->string('action'); + $table->json('actionPayload')->nullable(); + $table->string('evaluate')->default('before'); + $table->unsignedBigInteger('form_block_id'); + $table->timestamps(); + }); + } +}; diff --git a/database/migrations/2024_11_01_155436_fix_wrong_attribute_name.php b/database/migrations/2024_11_01_155436_fix_wrong_attribute_name.php new file mode 100644 index 00000000..a1105081 --- /dev/null +++ b/database/migrations/2024_11_01_155436_fix_wrong_attribute_name.php @@ -0,0 +1,17 @@ +renameColumn('actionPayload', 'action_payload'); + }); + } +}; diff --git a/mise.toml b/mise.toml new file mode 100644 index 00000000..39fb9608 --- /dev/null +++ b/mise.toml @@ -0,0 +1,12 @@ +[tools] +node = "18" + +[tasks.up] +run = [ + "docker run --rm -u \"$(id -u):$(id -g)\" -v \"$(pwd):/var/www/html\" -w \"/var/www/html\" laravelsail/php83-composer:latest composer install --ignore-platform-reqs", + "./vendor/bin/sail up -d", + "./vendor/bin/sail artisan migrate", + "./vendor/bin/sail artisan key:generate", + "npm ci", + "npm run dev", +] diff --git a/package-lock.json b/package-lock.json index 0a8c9669..7be605c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.0.1", "license": "GNU Affero General Public License v3.0", "dependencies": { - "@deck9/ui": "^0.15.0", + "@deck9/ui": "^0.15.5", "@headlessui/vue": "^1.7.23", "@highlightjs/vue-plugin": "^2.1.0", "@inertiajs/vue3": "^1.2.0", @@ -26,8 +26,8 @@ "@tiptap/vue-3": "^2.7.2", "@types/lodash": "^4.17.7", "@types/node": "^22.5.5", - "@typescript-eslint/eslint-plugin": "^8.5.0", - "@typescript-eslint/parser": "^8.5.0", + "@typescript-eslint/eslint-plugin": "^8.6.0", + "@typescript-eslint/parser": "^8.6.0", "@vitejs/plugin-vue": "^5.1.4", "@vueuse/core": "^11.1.0", "autoprefixer": "^10.4.20", @@ -63,7 +63,7 @@ "prettier": "^3.3.3", "prettier-eslint": "^16.3.0", "prettier-plugin-tailwindcss": "^0.6.6", - "puppeteer": "^23.3.0", + "puppeteer": "^23.4.0", "tailwind-config-viewer": "^2.0.4", "vite": "^5.4.6", "vitest": "^2.1.1" @@ -318,9 +318,9 @@ } }, "node_modules/@deck9/ui": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@deck9/ui/-/ui-0.15.0.tgz", - "integrity": "sha512-O3biiHnY/gjNQ77XkJzNEDkz1sEJ3Ory60h18aY4isX/ck8q9/3lnlmn1XfnWEwix9BuUkGMvpdLkrl+qY3Z0A==", + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@deck9/ui/-/ui-0.15.5.tgz", + "integrity": "sha512-9a7IuimXfCo6f3GwbGc6mfgU120zR3zJ/xQjHbIjcMqDdli02MCZIH++7SNElY1G9Cait/yisujnQOFawGrPWw==", "license": "MIT", "dependencies": { "@deck9/tailwindcss-recursive-font-helper": "^1.0.1", @@ -1072,13 +1072,13 @@ } }, "node_modules/@intlify/core-base": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.0.tgz", - "integrity": "sha512-zJn0imh9HIsZZUtt9v8T16PeVstPv6bP2YzlrYJwoF8F30gs4brZBwW2KK6EI5WYKFi3NeqX6+UU4gniz5TkGg==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.2.tgz", + "integrity": "sha512-DZyQ4Hk22sC81MP4qiCDuU+LdaYW91A6lCjq8AWPvY3+mGMzhGDfOCzvyR6YBQxtlPjFqMoFk9ylnNYRAQwXtQ==", "license": "MIT", "dependencies": { - "@intlify/message-compiler": "9.14.0", - "@intlify/shared": "9.14.0" + "@intlify/message-compiler": "9.14.2", + "@intlify/shared": "9.14.2" }, "engines": { "node": ">= 16" @@ -1088,12 +1088,12 @@ } }, "node_modules/@intlify/message-compiler": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.0.tgz", - "integrity": "sha512-sXNsoMI0YsipSXW8SR75drmVK56tnJHoYbPXUv2Cf9lz6FzvwsosFm6JtC1oQZI/kU+n7qx0qRrEWkeYFTgETA==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.2.tgz", + "integrity": "sha512-YsKKuV4Qv4wrLNsvgWbTf0E40uRv+Qiw1BeLQ0LAxifQuhiMe+hfTIzOMdWj/ZpnTDj4RSZtkXjJM7JDiiB5LQ==", "license": "MIT", "dependencies": { - "@intlify/shared": "9.14.0", + "@intlify/shared": "9.14.2", "source-map-js": "^1.0.2" }, "engines": { @@ -1104,9 +1104,9 @@ } }, "node_modules/@intlify/shared": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.0.tgz", - "integrity": "sha512-r+N8KRQL7LgN1TMTs1A2svfuAU0J94Wu9wWdJVJqYsoMMLIeJxrPjazihfHpmJqfgZq0ah3Y9Q4pgWV2O90Fyg==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.2.tgz", + "integrity": "sha512-uRAHAxYPeF+G5DBIboKpPgC/Waecd4Jz8ihtkpJQD5ycb5PwXp0k/+hBGl5dAjwF7w+l74kz/PKA8r8OK//RUw==", "license": "MIT", "engines": { "node": ">= 16" @@ -1454,7 +1454,6 @@ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", "dev": true, - "license": "Apache-2.0", "dependencies": { "debug": "^4.3.6", "extract-zip": "^2.0.1", @@ -1479,9 +1478,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz", - "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.4.tgz", + "integrity": "sha512-gGi5adZWvjtJU7Axs//CWaQbQd/vGy8KGcnEaCWiyCqxWYDxwIlAHFuSe6Guoxtd0SRvSfVTDMPd5H+4KE2kKA==", "cpu": [ "arm" ], @@ -1492,9 +1491,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz", - "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.4.tgz", + "integrity": "sha512-1aRlh1gqtF7vNPMnlf1vJKk72Yshw5zknR/ZAVh7zycRAGF2XBMVDAHmFQz/Zws5k++nux3LOq/Ejj1WrDR6xg==", "cpu": [ "arm64" ], @@ -1505,9 +1504,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz", - "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.4.tgz", + "integrity": "sha512-drHl+4qhFj+PV/jrQ78p9ch6A0MfNVZScl/nBps5a7u01aGf/GuBRrHnRegA9bP222CBDfjYbFdjkIJ/FurvSQ==", "cpu": [ "arm64" ], @@ -1518,9 +1517,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz", - "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.4.tgz", + "integrity": "sha512-hQqq/8QALU6t1+fbNmm6dwYsa0PDD4L5r3TpHx9dNl+aSEMnIksHZkSO3AVH+hBMvZhpumIGrTFj8XCOGuIXjw==", "cpu": [ "x64" ], @@ -1530,10 +1529,36 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.4.tgz", + "integrity": "sha512-/L0LixBmbefkec1JTeAQJP0ETzGjFtNml2gpQXA8rpLo7Md+iXQzo9kwEgzyat5Q+OG/C//2B9Fx52UxsOXbzw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.4.tgz", + "integrity": "sha512-6Rk3PLRK+b8L/M6m/x6Mfj60LhAUcLJ34oPaxufA+CfqkUrDoUPQYFdRrhqyOvtOKXLJZJwxlOLbQjNYQcRQfw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz", - "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.4.tgz", + "integrity": "sha512-kmT3x0IPRuXY/tNoABp2nDvI9EvdiS2JZsd4I9yOcLCCViKsP0gB38mVHOhluzx+SSVnM1KNn9k6osyXZhLoCA==", "cpu": [ "arm" ], @@ -1544,9 +1569,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz", - "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.4.tgz", + "integrity": "sha512-3iSA9tx+4PZcJH/Wnwsvx/BY4qHpit/u2YoZoXugWVfc36/4mRkgGEoRbRV7nzNBSCOgbWMeuQ27IQWgJ7tRzw==", "cpu": [ "arm" ], @@ -1557,9 +1582,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz", - "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.4.tgz", + "integrity": "sha512-7CwSJW+sEhM9sESEk+pEREF2JL0BmyCro8UyTq0Kyh0nu1v0QPNY3yfLPFKChzVoUmaKj8zbdgBxUhBRR+xGxg==", "cpu": [ "arm64" ], @@ -1570,9 +1595,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz", - "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.4.tgz", + "integrity": "sha512-GZdafB41/4s12j8Ss2izofjeFXRAAM7sHCb+S4JsI9vaONX/zQ8cXd87B9MRU/igGAJkKvmFmJJBeeT9jJ5Cbw==", "cpu": [ "arm64" ], @@ -1582,10 +1607,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.4.tgz", + "integrity": "sha512-uuphLuw1X6ur11675c2twC6YxbzyLSpWggvdawTUamlsoUv81aAXRMPBC1uvQllnBGls0Qt5Siw8reSIBnbdqQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz", - "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.4.tgz", + "integrity": "sha512-KvLEw1os2gSmD6k6QPCQMm2T9P2GYvsMZMRpMz78QpSoEevHbV/KOUbI/46/JRalhtSAYZBYLAnT9YE4i/l4vg==", "cpu": [ "ppc64" ], @@ -1596,9 +1634,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz", - "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.4.tgz", + "integrity": "sha512-wcpCLHGM9yv+3Dql/CI4zrY2mpQ4WFergD3c9cpRowltEh5I84pRT/EuHZsG0In4eBPPYthXnuR++HrFkeqwkA==", "cpu": [ "riscv64" ], @@ -1609,9 +1647,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz", - "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.4.tgz", + "integrity": "sha512-nLbfQp2lbJYU8obhRQusXKbuiqm4jSJteLwfjnunDT5ugBKdxqw1X9KWwk8xp1OMC6P5d0WbzxzhWoznuVK6XA==", "cpu": [ "s390x" ], @@ -1622,9 +1660,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz", - "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.4.tgz", + "integrity": "sha512-JGejzEfVzqc/XNiCKZj14eb6s5w8DdWlnQ5tWUbs99kkdvfq9btxxVX97AaxiUX7xJTKFA0LwoS0KU8C2faZRg==", "cpu": [ "x64" ], @@ -1635,9 +1673,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz", - "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.4.tgz", + "integrity": "sha512-/iFIbhzeyZZy49ozAWJ1ZR2KW6ZdYUbQXLT4O5n1cRZRoTpwExnHLjlurDXXPKEGxiAg0ujaR9JDYKljpr2fDg==", "cpu": [ "x64" ], @@ -1648,9 +1686,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz", - "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.4.tgz", + "integrity": "sha512-qORc3UzoD5UUTneiP2Afg5n5Ti1GAW9Gp5vHPxzvAFFA3FBaum9WqGvYXGf+c7beFdOKNos31/41PRMUwh1tpA==", "cpu": [ "arm64" ], @@ -1661,9 +1699,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz", - "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.4.tgz", + "integrity": "sha512-5g7E2PHNK2uvoD5bASBD9aelm44nf1w4I5FEI7MPHLWcCSrR8JragXZWgKPXk5i2FU3JFfa6CGZLw2RrGBHs2Q==", "cpu": [ "ia32" ], @@ -1674,9 +1712,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz", - "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.4.tgz", + "integrity": "sha512-p0scwGkR4kZ242xLPBuhSckrJ734frz6v9xZzD+kHVYRAkSUmdSLCIJRfql6H5//aF8Q10K+i7q8DiPfZp0b7A==", "cpu": [ "x64" ], @@ -2225,15 +2263,7 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/cors": { "version": "2.8.17", @@ -2246,9 +2276,10 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -2286,23 +2317,21 @@ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.5.0.tgz", - "integrity": "sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==", - "license": "MIT", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz", + "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/type-utils": "8.5.0", - "@typescript-eslint/utils": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/type-utils": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2326,15 +2355,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz", - "integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/typescript-estree": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz", + "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==", + "dependencies": { + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4" }, "engines": { @@ -2354,13 +2382,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz", - "integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==", - "license": "MIT", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz", + "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==", "dependencies": { - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0" + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2371,13 +2398,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.5.0.tgz", - "integrity": "sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==", - "license": "MIT", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz", + "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==", "dependencies": { - "@typescript-eslint/typescript-estree": "8.5.0", - "@typescript-eslint/utils": "8.5.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/utils": "8.6.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2395,10 +2421,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz", - "integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==", - "license": "MIT", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz", + "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2408,13 +2433,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz", - "integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==", - "license": "BSD-2-Clause", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz", + "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==", "dependencies": { - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2439,7 +2463,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2448,7 +2471,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2460,15 +2482,14 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz", - "integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==", - "license": "MIT", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz", + "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/typescript-estree": "8.5.0" + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2482,12 +2503,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz", - "integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==", - "license": "MIT", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz", + "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==", "dependencies": { - "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/types": "8.6.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -3406,7 +3426,6 @@ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -3500,8 +3519,7 @@ "version": "1.6.6", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", - "dev": true, - "license": "Apache-2.0" + "dev": true }, "node_modules/balanced-match": { "version": "1.0.2", @@ -3513,15 +3531,13 @@ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", "dev": true, - "license": "Apache-2.0", "optional": true }, "node_modules/bare-fs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.3.tgz", - "integrity": "sha512-7RYKL+vZVCyAsMLi5SPu7QGauGGT8avnP/HO571ndEuV4MYdGXvLhtW67FuLPeEI8EiIY7zbbRR9x7x7HU0kgw==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "dev": true, - "license": "Apache-2.0", "optional": true, "dependencies": { "bare-events": "^2.0.0", @@ -3530,11 +3546,10 @@ } }, "node_modules/bare-os": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz", - "integrity": "sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "dev": true, - "license": "Apache-2.0", "optional": true }, "node_modules/bare-path": { @@ -3542,22 +3557,20 @@ "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", "dev": true, - "license": "Apache-2.0", "optional": true, "dependencies": { "bare-os": "^2.1.0" } }, "node_modules/bare-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.1.tgz", - "integrity": "sha512-YTB47kHwBW9zSG8LD77MIBAAQXjU2WjAkMHeeb7hUplVs6+IoM5I7uEVQNPMB7lj9r8I76UMdoMkGnCodHOLqg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.0.tgz", + "integrity": "sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==", "dev": true, - "license": "Apache-2.0", "optional": true, "dependencies": { "b4a": "^1.6.6", - "streamx": "^2.18.0" + "streamx": "^2.20.0" } }, "node_modules/base64-js": { @@ -3578,8 +3591,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/base64id": { "version": "2.0.0", @@ -3596,7 +3608,6 @@ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -3642,14 +3653,14 @@ } }, "node_modules/browser-sync": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.2.tgz", - "integrity": "sha512-PC9c7aWJFVR4IFySrJxOqLwB9ENn3/TaXCXtAa0SzLwocLN3qMjN+IatbjvtCX92BjNXsY6YWg9Eb7F3Wy255g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.3.tgz", + "integrity": "sha512-91hoBHKk1C4pGeD+oE9Ld222k2GNQEAsI5AElqR8iLLWNrmZR2LPP8B0h8dpld9u7kro5IEUB3pUb0DJ3n1cRQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "browser-sync-client": "^3.0.2", - "browser-sync-ui": "^3.0.2", + "browser-sync-client": "^3.0.3", + "browser-sync-ui": "^3.0.3", "bs-recipes": "1.3.4", "chalk": "4.1.2", "chokidar": "^3.5.1", @@ -3663,15 +3674,15 @@ "fs-extra": "3.0.1", "http-proxy": "^1.18.1", "immutable": "^3", - "micromatch": "^4.0.2", + "micromatch": "^4.0.8", "opn": "5.3.0", "portscanner": "2.2.0", "raw-body": "^2.3.2", "resp-modifier": "6.0.2", "rx": "4.1.0", - "send": "0.16.2", - "serve-index": "1.9.1", - "serve-static": "1.13.2", + "send": "^0.19.0", + "serve-index": "^1.9.1", + "serve-static": "^1.16.2", "server-destroy": "1.0.1", "socket.io": "^4.4.1", "ua-parser-js": "^1.0.33", @@ -3685,9 +3696,9 @@ } }, "node_modules/browser-sync-client": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.2.tgz", - "integrity": "sha512-tBWdfn9L0wd2Pjuz/NWHtNEKthVb1Y67vg8/qyGNtCqetNz5lkDkFnrsx5UhPNPYUO8vci50IWC/BhYaQskDiQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.3.tgz", + "integrity": "sha512-TOEXaMgYNjBYIcmX5zDlOdjEqCeCN/d7opf/fuyUD/hhGVCfP54iQIDhENCi012AqzYZm3BvuFl57vbwSTwkSQ==", "dev": true, "license": "ISC", "dependencies": { @@ -3700,9 +3711,9 @@ } }, "node_modules/browser-sync-ui": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.2.tgz", - "integrity": "sha512-V3FwWAI+abVbFLTyJjXJlCMBwjc3GXf/BPGfwO2fMFACWbIGW9/4SrBOFYEOOtqzCjQE0Di+U3VIb7eES4omNA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.3.tgz", + "integrity": "sha512-FcGWo5lP5VodPY6O/f4pXQy5FFh4JK0f2/fTBsp0Lx1NtyBWs/IfPPJbW8m1ujTW/2r07oUXKTF2LYZlCZktjw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3715,15 +3726,6 @@ "stream-throttle": "^0.1.3" } }, - "node_modules/browser-sync/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, "node_modules/browser-sync/node_modules/fs-extra": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", @@ -3735,27 +3737,6 @@ "universalify": "^0.1.0" } }, - "node_modules/browser-sync/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/browser-sync/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, "node_modules/browser-sync/node_modules/jsonfile": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", @@ -3765,75 +3746,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/browser-sync/node_modules/mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true, - "bin": { - "mime": "cli.js" - } - }, - "node_modules/browser-sync/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/browser-sync/node_modules/send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/browser-sync/node_modules/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/browser-sync/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/browser-sync/node_modules/statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/browser-sync/node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -3900,7 +3812,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -3911,7 +3822,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } @@ -3989,9 +3899,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001658", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001658.tgz", - "integrity": "sha512-N2YVqWbJELVdrnsW5p+apoQyYt51aBMSsBZki1XZEfeBCexcM/sf4xiAHcXQBkuOwJBXtWF7aW1sYX6tKebPHw==", + "version": "1.0.30001697", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz", + "integrity": "sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ==", "funding": [ { "type": "opencollective", @@ -4090,7 +4000,6 @@ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz", "integrity": "sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "mitt": "3.0.1", "urlpattern-polyfill": "10.0.0", @@ -4104,8 +4013,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/co": { "version": "4.6.0", @@ -4271,9 +4179,9 @@ } }, "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, "license": "MIT", "engines": { @@ -4360,9 +4268,10 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4414,7 +4323,6 @@ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 14" } @@ -4511,7 +4419,6 @@ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, - "license": "MIT", "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -4545,10 +4452,15 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/dev-ip": { "version": "1.0.1", @@ -4563,11 +4475,10 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1330662", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz", - "integrity": "sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==", - "dev": true, - "license": "BSD-3-Clause" + "version": "0.0.1342118", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1342118.tgz", + "integrity": "sha512-75fMas7PkYNDTmDyb6PRJCH7ILmHLp+BhrZGeMsa4bCh40DTxgCz2NRy5UDzII4C5KuD0oBMZ9vXKhEl6UD/3w==", + "dev": true }, "node_modules/didyoumean": { "version": "1.2.2", @@ -4716,24 +4627,22 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", "dev": true, "license": "MIT", "dependencies": { - "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", @@ -4744,9 +4653,9 @@ } }, "node_modules/engine.io-client": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", - "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", "dev": true, "license": "MIT", "dependencies": { @@ -4754,7 +4663,7 @@ "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.0.0" + "xmlhttprequest-ssl": "~2.1.1" } }, "node_modules/engine.io-client/node_modules/ws": { @@ -4945,7 +4854,6 @@ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -5120,7 +5028,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -5176,8 +5083,9 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5202,7 +5110,6 @@ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -5227,8 +5134,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-glob": { "version": "3.3.2", @@ -5268,7 +5174,6 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "license": "MIT", "dependencies": { "pend": "~1.2.0" } @@ -5398,7 +5303,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5476,7 +5380,6 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -5492,7 +5395,6 @@ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "dev": true, - "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -5832,8 +5734,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/ignore": { "version": "5.3.2", @@ -5919,7 +5820,6 @@ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, - "license": "MIT", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -6284,8 +6184,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/jsdom": { "version": "25.0.0", @@ -6808,6 +6707,19 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.49.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", @@ -6904,15 +6816,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6945,7 +6858,6 @@ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -7139,7 +7051,6 @@ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.0.2", @@ -7159,7 +7070,6 @@ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, - "license": "MIT", "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -7278,10 +7188,11 @@ } }, "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", - "dev": true + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", @@ -7314,8 +7225,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/picocolors": { "version": "1.1.0", @@ -7879,7 +7789,6 @@ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -8106,7 +8015,6 @@ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -8126,7 +8034,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "license": "ISC", "engines": { "node": ">=12" } @@ -8144,11 +8051,10 @@ "license": "MIT" }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, - "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -8172,18 +8078,17 @@ } }, "node_modules/puppeteer": { - "version": "23.3.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.3.0.tgz", - "integrity": "sha512-e2jY8cdWSUGsrLxqGm3hIbJq/UIk1uOY8XY7SM51leXkH7shrIyE91lK90Q9byX6tte+cyL3HKqlWBEd6TjWTA==", + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.4.0.tgz", + "integrity": "sha512-FxgFFJI7NAsX8uebiEDSjS86vufz9TaqERQHShQT0lCbSRI3jUPEcz/0HdwLiYvfYNsc1zGjqY3NsGZya4PvUA==", "dev": true, "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.4.0", "chromium-bidi": "0.6.5", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1330662", - "puppeteer-core": "23.3.0", + "devtools-protocol": "0.0.1342118", + "puppeteer-core": "23.4.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -8194,16 +8099,15 @@ } }, "node_modules/puppeteer-core": { - "version": "23.3.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.3.0.tgz", - "integrity": "sha512-sB2SsVMFs4gKad5OCdv6w5vocvtEUrRl0zQqSyRPbo/cj1Ktbarmhxy02Zyb9R9HrssBcJDZbkrvBnbaesPyYg==", + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.4.0.tgz", + "integrity": "sha512-fqkIP5FOcb38jfBj/OcBz1wFaI9nk40uQKSORvnXws6wCbep2dg8yxZ3ddJxBIfQsxoiEOvnrykFinUScrB/ew==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.4.0", "chromium-bidi": "0.6.5", - "debug": "^4.3.6", - "devtools-protocol": "0.0.1330662", + "debug": "^4.3.7", + "devtools-protocol": "0.0.1342118", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -8255,8 +8159,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/randombytes": { "version": "2.1.0", @@ -8272,6 +8175,7 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8485,12 +8389,12 @@ } }, "node_modules/rollup": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz", - "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==", + "version": "4.34.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.4.tgz", + "integrity": "sha512-spF66xoyD7rz3o08sHP7wogp1gZ6itSq22SGa/IZTcUDXDlOyrShwMwkVSB+BUxFRZZCUYqdb3KWDEOMVQZxuw==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -8500,22 +8404,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.2", - "@rollup/rollup-android-arm64": "4.21.2", - "@rollup/rollup-darwin-arm64": "4.21.2", - "@rollup/rollup-darwin-x64": "4.21.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.2", - "@rollup/rollup-linux-arm-musleabihf": "4.21.2", - "@rollup/rollup-linux-arm64-gnu": "4.21.2", - "@rollup/rollup-linux-arm64-musl": "4.21.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2", - "@rollup/rollup-linux-riscv64-gnu": "4.21.2", - "@rollup/rollup-linux-s390x-gnu": "4.21.2", - "@rollup/rollup-linux-x64-gnu": "4.21.2", - "@rollup/rollup-linux-x64-musl": "4.21.2", - "@rollup/rollup-win32-arm64-msvc": "4.21.2", - "@rollup/rollup-win32-ia32-msvc": "4.21.2", - "@rollup/rollup-win32-x64-msvc": "4.21.2", + "@rollup/rollup-android-arm-eabi": "4.34.4", + "@rollup/rollup-android-arm64": "4.34.4", + "@rollup/rollup-darwin-arm64": "4.34.4", + "@rollup/rollup-darwin-x64": "4.34.4", + "@rollup/rollup-freebsd-arm64": "4.34.4", + "@rollup/rollup-freebsd-x64": "4.34.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.4", + "@rollup/rollup-linux-arm-musleabihf": "4.34.4", + "@rollup/rollup-linux-arm64-gnu": "4.34.4", + "@rollup/rollup-linux-arm64-musl": "4.34.4", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.4", + "@rollup/rollup-linux-riscv64-gnu": "4.34.4", + "@rollup/rollup-linux-s390x-gnu": "4.34.4", + "@rollup/rollup-linux-x64-gnu": "4.34.4", + "@rollup/rollup-linux-x64-musl": "4.34.4", + "@rollup/rollup-win32-arm64-msvc": "4.34.4", + "@rollup/rollup-win32-ia32-msvc": "4.34.4", + "@rollup/rollup-win32-x64-msvc": "4.34.4", "fsevents": "~2.3.2" } }, @@ -8635,6 +8542,108 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz", + "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -8704,6 +8713,134 @@ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-static/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-static/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/server-destroy": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", @@ -8791,7 +8928,6 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -8803,9 +8939,9 @@ "integrity": "sha512-Dndj/MOG7VP83mvzfGCLGzV2HuK1lWachMtWl/Iuk6zV7noDycIBnflwaPuDzoaapEl3Pc4+ybJArkkx9sxPZg==" }, "node_modules/socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", "dev": true, "license": "MIT", "dependencies": { @@ -8813,7 +8949,7 @@ "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.2", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" }, @@ -8855,15 +8991,15 @@ } }, "node_modules/socket.io-client": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", - "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", "dev": true, "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", - "engine.io-client": "~6.5.2", + "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" }, "engines": { @@ -8889,7 +9025,6 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, - "license": "MIT", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -8904,7 +9039,6 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^7.1.1", "debug": "^4.3.4", @@ -8945,8 +9079,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, "node_modules/stackback": { "version": "0.0.2", @@ -8996,11 +9129,10 @@ "license": "MIT" }, "node_modules/streamx": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz", - "integrity": "sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ==", + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", "dev": true, - "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", "queue-tick": "^1.0.1", @@ -9289,7 +9421,6 @@ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", "dev": true, - "license": "MIT", "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" @@ -9304,7 +9435,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, - "license": "MIT", "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -9370,11 +9500,10 @@ "peer": true }, "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", + "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" } @@ -9407,8 +9536,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/tiny-case": { "version": "1.0.3", @@ -9562,8 +9690,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "dev": true, - "license": "0BSD" + "dev": true }, "node_modules/tsscmp": { "version": "1.0.6", @@ -9613,8 +9740,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/typescript": { "version": "5.6.2", @@ -9659,7 +9785,6 @@ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, - "license": "MIT", "dependencies": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -9742,8 +9867,7 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -9781,9 +9905,9 @@ } }, "node_modules/vite": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", - "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "license": "MIT", "dependencies": { "esbuild": "^0.21.3", @@ -10000,13 +10124,13 @@ } }, "node_modules/vue-i18n": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.0.tgz", - "integrity": "sha512-LxmpRuCt2rI8gqU+kxeflRZMQn4D5+4M3oP3PWZdowW/ePJraHqhF7p4CuaME52mUxdw3Mmy2yAUKgfZYgCRjA==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.2.tgz", + "integrity": "sha512-JK9Pm80OqssGJU2Y6F7DcM8RFHqVG4WkuCqOZTVsXkEzZME7ABejAUqUdA931zEBedc4thBgSUWxeQh4uocJAQ==", "license": "MIT", "dependencies": { - "@intlify/core-base": "9.14.0", - "@intlify/shared": "9.14.0", + "@intlify/core-base": "9.14.2", + "@intlify/shared": "9.14.2", "@vue/devtools-api": "^6.5.0" }, "engines": { @@ -10337,9 +10461,9 @@ "dev": true }, "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", "dev": true, "engines": { "node": ">=0.4.0" @@ -10408,7 +10532,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, - "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -10481,7 +10604,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index b2054c84..fa8f850a 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,13 @@ "prettier": "^3.3.3", "prettier-eslint": "^16.3.0", "prettier-plugin-tailwindcss": "^0.6.6", - "puppeteer": "^23.3.0", + "puppeteer": "^23.4.0", "tailwind-config-viewer": "^2.0.4", "vite": "^5.4.6", "vitest": "^2.1.1" }, "dependencies": { - "@deck9/ui": "^0.15.0", + "@deck9/ui": "^0.15.5", "@headlessui/vue": "^1.7.23", "@highlightjs/vue-plugin": "^2.1.0", "@inertiajs/vue3": "^1.2.0", @@ -49,8 +49,8 @@ "@tiptap/vue-3": "^2.7.2", "@types/lodash": "^4.17.7", "@types/node": "^22.5.5", - "@typescript-eslint/eslint-plugin": "^8.5.0", - "@typescript-eslint/parser": "^8.5.0", + "@typescript-eslint/eslint-plugin": "^8.6.0", + "@typescript-eslint/parser": "^8.6.0", "@vitejs/plugin-vue": "^5.1.4", "@vueuse/core": "^11.1.0", "autoprefixer": "^10.4.20", diff --git a/resources/css/_typography.css b/resources/css/_typography.css index 26f11af1..23ab6fce 100644 --- a/resources/css/_typography.css +++ b/resources/css/_typography.css @@ -64,4 +64,9 @@ img { @apply max-w-full my-4; } +} + + +pre { + @apply bg-grey-900 text-xxs p-4 rounded text-white leading-none my-2; } \ No newline at end of file diff --git a/resources/js/Pages/PrivacyPolicy.vue b/resources/js/Pages/PrivacyPolicy.vue index e186ed5b..0ce85cd3 100644 --- a/resources/js/Pages/PrivacyPolicy.vue +++ b/resources/js/Pages/PrivacyPolicy.vue @@ -10,7 +10,7 @@
diff --git a/resources/js/Pages/Teams/MissingTeam.vue b/resources/js/Pages/Teams/MissingTeam.vue index 858079e4..3ffbe542 100644 --- a/resources/js/Pages/Teams/MissingTeam.vue +++ b/resources/js/Pages/Teams/MissingTeam.vue @@ -22,7 +22,7 @@

To begin using Input, you need to be part of a team. Currently, you're diff --git a/resources/js/Pages/TermsOfService.vue b/resources/js/Pages/TermsOfService.vue index f1d94662..4e7695bf 100644 --- a/resources/js/Pages/TermsOfService.vue +++ b/resources/js/Pages/TermsOfService.vue @@ -10,7 +10,7 @@

diff --git a/resources/js/api/conversation.ts b/resources/js/api/conversation.ts index 4fc79934..cf56fdfd 100644 --- a/resources/js/api/conversation.ts +++ b/resources/js/api/conversation.ts @@ -1,108 +1,99 @@ -/* eslint-disable no-async-promise-executor */ import { AxiosProgressEvent, AxiosResponse } from "axios"; import handler from "./handler"; import { useRoutes } from "@/utils/useRoutes"; -export function callGetForm( +export async function callGetForm( uuid: string, ): Promise> { - return new Promise(async (resolve, reject) => { - try { - const { route } = await useRoutes(); + try { + const { route } = await useRoutes(); - const resolvedRoute = route("api.public.forms.show", { - uuid, - }); + const resolvedRoute = route("api.public.forms.show", { + uuid, + }); - if (resolvedRoute) { - const response = await handler.get(resolvedRoute); - resolve(response as AxiosResponse); - } else { - reject("route not found"); - } - } catch (error) { - reject(error); + if (resolvedRoute) { + const response = await handler.get(resolvedRoute); + return Promise.resolve(response as AxiosResponse); + } else { + return Promise.reject("route not found"); } - }); + } catch (error) { + return Promise.reject(error); + } } -export function callGetFormStoryboard( +export async function callGetFormStoryboard( uuid: string, ): Promise> { - return new Promise(async (resolve, reject) => { - try { - const { route } = await useRoutes(); + try { + const { route } = await useRoutes(); - const resolvedRoute = route("api.public.forms.storyboard", { - uuid, - }); + const resolvedRoute = route("api.public.forms.storyboard", { + uuid, + }); - if (resolvedRoute) { - const response = await handler.get(resolvedRoute); - resolve(response as AxiosResponse); - } else { - reject("route not found"); - } - } catch (error) { - reject(error); + if (resolvedRoute) { + const response = await handler.get(resolvedRoute); + return Promise.resolve(response as AxiosResponse); + } else { + return Promise.reject("route not found"); } - }); + } catch (error) { + return Promise.reject(error); + } } -export function callCreateFormSession( +export async function callCreateFormSession( uuid: string, params: Record, ): Promise> { - return new Promise(async (resolve, reject) => { - try { - const { route } = await useRoutes(); + try { + const { route } = await useRoutes(); - const resolvedRoute = route("api.public.forms.session.create", { - uuid, - }); + const resolvedRoute = route("api.public.forms.session.create", { + uuid, + }); - if (resolvedRoute) { - const response = await handler.post(resolvedRoute, { - params, - }); - resolve(response as AxiosResponse); - } else { - reject("route not found"); - } - } catch (error) { - reject(error); + if (resolvedRoute) { + const response = await handler.post(resolvedRoute, { + params, + }); + return Promise.resolve(response as AxiosResponse); + } else { + return Promise.reject("route not found"); } - }); + } catch (error) { + return Promise.reject(error); + } } -export function callSubmitForm( +export async function callSubmitForm( uuid: string, token: string, payload: FormSubmitPayload | null, is_uploading: boolean = false, ): Promise> { - return new Promise(async (resolve, reject) => { - try { - const { route } = await useRoutes(); + try { + const { route } = await useRoutes(); - const resolvedRoute = route("api.public.forms.submit", { - uuid, - }); + const resolvedRoute = route("api.public.forms.submit", { + uuid, + }); - if (resolvedRoute) { - const response = await handler.post(resolvedRoute, { - token, - payload, - is_uploading, - }); - resolve(response as AxiosResponse); - } else { - reject("route not found"); - } - } catch (error) { - reject(error); + if (resolvedRoute) { + const response = await handler.post(resolvedRoute, { + token, + payload, + is_uploading, + }); + return Promise.resolve(response as AxiosResponse); + } else { + return Promise.reject("route not found"); } - }); + } catch (error) { + return Promise.reject(error); + } } export async function callUploadFiles( diff --git a/resources/js/api/logics.ts b/resources/js/api/logics.ts new file mode 100644 index 00000000..f8f78b30 --- /dev/null +++ b/resources/js/api/logics.ts @@ -0,0 +1,58 @@ +/* eslint-disable no-async-promise-executor */ +import { AxiosResponse } from "axios"; +import handler from "./handler"; + +export function callCreateFormBlockLogic( + id: number, + attributes: Partial, +): Promise> { + return new Promise(async (resolve, reject) => { + try { + const response = await handler.post( + window.route("api.logics.create", { block: id }), + { + ...attributes, + }, + ); + + resolve(response as AxiosResponse); + } catch (error) { + reject(error); + } + }); +} + +export function callUpdateFormBlockLogic( + attributes: Partial, +): Promise> { + return new Promise(async (resolve, reject) => { + try { + const response = await handler.post( + window.route("api.logics.update", { logic: attributes.id }), + { + ...attributes, + }, + ); + + resolve(response as AxiosResponse); + } catch (error) { + reject(error); + } + }); +} + +export function callDeleteFormBlockLogic( + attributes: Partial, +): Promise> { + return new Promise(async (resolve, reject) => { + try { + const response = await handler.delete( + window.route("api.logics.delete", { logic: attributes.id }), + ); + + resolve(response as AxiosResponse); + } catch (error) { + reject(error); + } + }); +} diff --git a/resources/js/components/Factory/Main/BlockType.vue b/resources/js/components/Factory/Main/BlockType.vue index 48e3d2f8..06027856 100644 --- a/resources/js/components/Factory/Main/BlockType.vue +++ b/resources/js/components/Factory/Main/BlockType.vue @@ -18,22 +18,13 @@ /> -
- -
- -
-
diff --git a/resources/js/components/Factory/Sidebar/Block.vue b/resources/js/components/Factory/Sidebar/Block.vue index 117dde16..dd5b3b64 100644 --- a/resources/js/components/Factory/Sidebar/Block.vue +++ b/resources/js/components/Factory/Sidebar/Block.vue @@ -1,14 +1,15 @@ @@ -71,13 +69,14 @@ import { computed, provide } from "vue"; import ConsentBlockMessage from "./ConsentBlockMessage.vue"; import DefaultBlockMessage from "./DefaultBlockMessage.vue"; import BlockInteraction from "./BlockInteraction.vue"; -import Label from "@/components/Label.vue"; +import BlockFooter from "./BlockFooter.vue"; +import BlockLogicVisualizer from "./BlockLogicVisualizer.vue"; +import InsertAfterButton from "./InsertAfterButton.vue"; import { useWorkbench, useForm } from "@/stores"; import { D9Menu, D9MenuLink } from "@deck9/ui"; import copy from "copy-text-to-clipboard"; import useActiveInteractions from "../Shared/useActiveInteractions"; import { useActiveCard } from "@/utils/useActiveCard"; -import InsertAfterButton from "./InsertAfterButton.vue"; import { storeToRefs } from "pinia"; const workbench = useWorkbench(); @@ -113,10 +112,6 @@ const deleteBlock = () => { } }; -const disableBlock = () => { - store.disableFormBlock(props.block, !props.block.is_disabled); -}; - const copyId = () => { copy(props.block.uuid); }; diff --git a/resources/js/components/Factory/Sidebar/BlockFooter.vue b/resources/js/components/Factory/Sidebar/BlockFooter.vue new file mode 100644 index 00000000..6b3e8f24 --- /dev/null +++ b/resources/js/components/Factory/Sidebar/BlockFooter.vue @@ -0,0 +1,89 @@ + + + diff --git a/resources/js/components/Factory/Sidebar/BlockLogicEditor.vue b/resources/js/components/Factory/Sidebar/BlockLogicEditor.vue new file mode 100644 index 00000000..39ba8949 --- /dev/null +++ b/resources/js/components/Factory/Sidebar/BlockLogicEditor.vue @@ -0,0 +1,140 @@ + + + diff --git a/resources/js/components/Factory/Sidebar/BlockLogicRule.vue b/resources/js/components/Factory/Sidebar/BlockLogicRule.vue new file mode 100644 index 00000000..c959829b --- /dev/null +++ b/resources/js/components/Factory/Sidebar/BlockLogicRule.vue @@ -0,0 +1,277 @@ + + + diff --git a/resources/js/components/Factory/Sidebar/BlockLogicVisualizer.vue b/resources/js/components/Factory/Sidebar/BlockLogicVisualizer.vue new file mode 100644 index 00000000..69c3738f --- /dev/null +++ b/resources/js/components/Factory/Sidebar/BlockLogicVisualizer.vue @@ -0,0 +1,79 @@ + + + diff --git a/resources/js/components/Factory/Sidebar/Group.vue b/resources/js/components/Factory/Sidebar/Group.vue index db32009e..0c58887a 100644 --- a/resources/js/components/Factory/Sidebar/Group.vue +++ b/resources/js/components/Factory/Sidebar/Group.vue @@ -1,30 +1,27 @@ diff --git a/resources/js/forms/classic.ts b/resources/js/forms/classic.ts index f0f446ad..3334d851 100644 --- a/resources/js/forms/classic.ts +++ b/resources/js/forms/classic.ts @@ -34,7 +34,7 @@ const i18n = createI18n({ const formId = document.currentScript?.getAttribute("data-form"); let mountElement: Element | string | null = document.querySelector( - `#${formId}-wrapper` + `#${formId}-wrapper`, ); if (!mountElement) { diff --git a/resources/js/forms/classic/ClassicForm.vue b/resources/js/forms/classic/ClassicForm.vue index 34ab1951..51ddcfae 100644 --- a/resources/js/forms/classic/ClassicForm.vue +++ b/resources/js/forms/classic/ClassicForm.vue @@ -42,7 +42,6 @@ { }); provide("disableFocus", focusDisabled); +// init routes +await useRoutes(); + +// init conversation store const store = useConversation(); await store.initForm( props.settings, diff --git a/resources/js/forms/classic/layout/FormSubmittedPage.vue b/resources/js/forms/classic/layout/FormSubmittedPage.vue index 476a1048..32b1bae7 100644 --- a/resources/js/forms/classic/layout/FormSubmittedPage.vue +++ b/resources/js/forms/classic/layout/FormSubmittedPage.vue @@ -1,23 +1,23 @@ diff --git a/resources/js/forms/classic/layout/Navigator.vue b/resources/js/forms/classic/layout/Navigator.vue index 3a09ab8e..3457d114 100644 --- a/resources/js/forms/classic/layout/Navigator.vue +++ b/resources/js/forms/classic/layout/Navigator.vue @@ -55,13 +55,12 @@ const store = useConversation(); const { t } = useI18n(); -const props = defineProps<{ +defineProps<{ hideNavigation?: boolean; - block?: PublicFormBlockModel | null; }>(); -const { actionValidator } = props.block - ? useActions(props.block) +const { actionValidator } = store.currentBlock + ? useActions(store.currentBlock) : { actionValidator: () => { return { valid: true }; @@ -75,14 +74,18 @@ const validator = computed(() => { }); const totalPages = computed(() => { - return store.queue?.length ?? 0; + return store.processedQueue?.length ?? 0; }); const currentPage = computed(() => { - return store.current + 1; + return store.currentBlockIndex + 1; }); const progress = computed(() => { - return Math.round((store.current / totalPages.value) * 100); + if (store.currentBlockIndex <= 0) { + return 0; + } + + return Math.round(((store.currentBlockIndex + 1) / totalPages.value) * 100); }); diff --git a/resources/js/stores/conversation.ts b/resources/js/stores/conversation.ts index 9dbabdd0..0a20d9c6 100644 --- a/resources/js/stores/conversation.ts +++ b/resources/js/stores/conversation.ts @@ -6,6 +6,8 @@ import { callGetForm, callUploadFiles, } from "@/api/conversation"; +import { evaluateGotoLogic, isBlockVisible } from "./helpers/logic"; +import { createFlatQueue } from "./helpers/queue"; import { Ref, ref } from "vue"; type ConversationStore = { @@ -13,7 +15,7 @@ type ConversationStore = { session?: FormSessionModel; storyboard: PublicFormBlockModel[] | null; queue: PublicFormBlockModel[] | null; - current: number; + current: PublicFormBlockModel["id"] | null; payload: FormSubmitPayload; isProcessing: boolean; isSubmitted: boolean; @@ -28,7 +30,7 @@ export const useConversation = defineStore("form", { session: undefined, storyboard: null, queue: null, - current: 0, + current: null, payload: {}, isProcessing: false, isSubmitted: false, @@ -38,37 +40,62 @@ export const useConversation = defineStore("form", { }, getters: { - isFirstBlock(state): boolean { - if (!state.queue) { + isFirstBlock(): boolean { + if (!this.processedQueue) { return false; } - return state.current === 0; + return this.currentBlockIndex === 0; }, - isLastBlock(state): boolean { - if (!state.queue) { + isLastBlock(): boolean { + if (!this.processedQueue) { return false; } - return state.current + 1 >= state.queue.length; + return this.currentBlockIndex + 1 >= this.processedQueue.length; }, - currentBlock: (state): PublicFormBlockModel | null => { - if (state.queue && state.queue.length >= state.current) { - return state.queue[state.current]; + processedQueue(state): PublicFormBlockModel[] { + if (!state.queue) { + return []; } - return null; + return state.queue + .filter((block) => isBlockVisible(block, state.payload)) + .filter((block) => block.type !== "group"); + }, + + currentBlockIndex(state): number { + return this.processedQueue.findIndex( + (block) => block.id === state.current, + ); + }, + + currentBlock(): PublicFormBlockModel | null { + if (!this.processedQueue || !this.processedQueue.length) { + return null; + } + + if (this.currentBlockIndex === -1) { + return null; + } + + try { + return this.processedQueue[this.currentBlockIndex]; + } catch (e) { + console.warn("Current block not found in processed queue", e); + return null; + } }, currentPayload( state, ): FormBlockInteractionPayload | FormBlockInteractionPayload[] | null { - if (!this.currentBlock) return null; + if (!state.current) return null; - if (state.payload[this.currentBlock.id]) { - return state.payload[this.currentBlock.id]; + if (state.payload[state.current]) { + return state.payload[state.current]; } return null; @@ -137,19 +164,10 @@ export const useConversation = defineStore("form", { return ref( !state.isSubmitted && state.payload && - Object.keys(state.payload).length > 0 && - state.current > 0, + Object.keys(state.payload).length > 0, ); }, - currentBlockIdentifier(): string | null { - if (!this.currentBlock) { - return null; - } - - return this.currentBlock.title || this.currentBlock.id; - }, - callToActionUrl(state): string | null { if (!state.form || !state.session) { return null; @@ -251,21 +269,16 @@ export const useConversation = defineStore("form", { } } - const storyboardResponse = await callGetFormStoryboard(id); - const formSessionResponse = await callCreateFormSession(id, params); + const [formSessionResponse, storyboardResponse] = await Promise.all( + [callCreateFormSession(id, params), callGetFormStoryboard(id)], + ); this.session = formSessionResponse.data; this.storyboard = storyboardResponse.data.blocks; - this.queue = this.storyboard.filter((block) => { - return block.parent_block === null; - }); + this.queue = createFlatQueue(this.storyboard); - // if the first block is a group, we need to evaluate it - if (this.currentBlock?.type === "group") { - this.evaluateGroupBlock(this.currentBlock); - this.next(); - } + this.current = this.processedQueue[0].id ?? null; }, enableInputMode() { @@ -280,9 +293,9 @@ export const useConversation = defineStore("form", { action: PublicFormBlockInteractionModel, value: string | boolean | number | File[] | null, ) { - if (!this.currentBlock) return; + if (!this.current) return; - this.payload[this.currentBlock.id] = { + this.payload[this.current] = { payload: value, actionId: action.id, }; @@ -297,16 +310,16 @@ export const useConversation = defineStore("form", { | null, keepChecked: boolean | null = null, ) { - if (!this.currentBlock) return; + if (!this.current) return; const givenPayload = { payload: value, actionId: action.id, }; - const currentPayload = this.payload[this.currentBlock.id]; + const currentPayload = this.payload[this.current]; if (!Array.isArray(currentPayload)) { - this.payload[this.currentBlock.id] = [givenPayload]; + this.payload[this.current] = [givenPayload]; } else { const foundIndex = currentPayload.findIndex( (p) => p.actionId === action.id, @@ -324,17 +337,39 @@ export const useConversation = defineStore("form", { } }, + findBlockIndex(blockId: string): number { + return this.processedQueue.findIndex( + (block) => block.id === blockId, + ); + }, + + goToIndex(index: number) { + if (index >= 0 && index < this.processedQueue.length) { + this.current = this.processedQueue[index].id; + } else { + console.warn("Index out of bounds", index); + } + }, + + executeGotoAction(targetBlockId: string): boolean { + const targetIndex = this.findBlockIndex(targetBlockId); + if (targetIndex !== -1) { + this.goToIndex(targetIndex); + return true; + } else { + console.warn( + `Target block ${targetBlockId} not found in processed queue. Please review your block logic to ensure the target block is visible.`, + ); + return false; + } + }, + back() { if (this.isFirstBlock) { return; } - this.current -= 1; - - // if we are on a group block, we need to go back again - if (this.currentBlock?.type === "group") { - this.back(); - } + this.goToIndex(this.currentBlockIndex - 1); }, /** @@ -342,6 +377,16 @@ export const useConversation = defineStore("form", { * @returns {Promise} */ async next(): Promise { + const gotoAction = this.currentBlock + ? evaluateGotoLogic(this.currentBlock, this.payload) + : null; + + if (gotoAction && gotoAction.target) { + if (this.executeGotoAction(gotoAction.target)) { + return Promise.resolve(false); + } + } + if (this.isLastBlock) { this.uploads = {}; this.isProcessing = true; @@ -400,15 +445,7 @@ export const useConversation = defineStore("form", { return Promise.reject(new Error("Form or session not set")); } } else { - this.current += 1; - - // need to check if the next block is a group block - if (this.currentBlock?.type === "group") { - this.evaluateGroupBlock(this.currentBlock); - - // we call next, since the group block has no other action - return this.next(); - } + this.goToIndex(this.currentBlockIndex + 1); return Promise.resolve(false); } @@ -424,23 +461,5 @@ export const useConversation = defineStore("form", { }); }); }, - - evaluateGroupBlock(currentBlock: PublicFormBlockModel) { - // we first should remove all blocks related to that group - this.queue = - this.queue?.filter((block) => { - return block.parent_block !== currentBlock?.id; - }) ?? []; - - // now we find all children from the storyboard - const children = this.storyboard?.filter((block) => { - return block.parent_block === currentBlock?.id; - }); - - // we add the children to the queue - if (children && children?.length > 0) { - this.queue?.splice(this.current + 1, 0, ...children); - } - }, }, }); diff --git a/resources/js/stores/form.ts b/resources/js/stores/form.ts index f3b45163..107cb9a7 100644 --- a/resources/js/stores/form.ts +++ b/resources/js/stores/form.ts @@ -24,6 +24,7 @@ interface FormStore { mapping: Record | null; isCssTransitionEnabled: boolean; isBlockMenuEnabled: boolean; + showLogicInStoryboard: boolean; } export const useForm = defineStore("form", { @@ -34,6 +35,7 @@ export const useForm = defineStore("form", { blocks: null, isCssTransitionEnabled: true, isBlockMenuEnabled: true, + showLogicInStoryboard: true, }; }, @@ -81,6 +83,28 @@ export const useForm = defineStore("form", { showBlockMenus: (state): boolean => { return state.isBlockMenuEnabled; }, + + blocksTree: (state): TreeNode[] => { + const tree: TreeNode[] = []; + const map: Record = {}; + + state.blocks?.forEach((block) => { + map[block.uuid] = { block, children: [] }; + }); + + state.blocks?.forEach((block) => { + if (block.parent_block) { + const parent = map[block.parent_block]; + if (parent) { + parent.children.push(map[block.uuid]); + } + } else { + tree.push(map[block.uuid]); + } + }); + + return tree; + }, }, actions: { @@ -101,6 +125,10 @@ export const useForm = defineStore("form", { this.isBlockMenuEnabled = false; }, + toggleLogicInStoryboard() { + this.showLogicInStoryboard = !this.showLogicInStoryboard; + }, + async refreshForm(includeSubmissions = false) { if (this.form) { const response = await callGetForm(this.form); @@ -281,7 +309,11 @@ export const useForm = defineStore("form", { } }, - async disableFormBlock(block: FormBlockModel, newValue: boolean) { + async updateFormBlockProperty( + block: FormBlockModel, + property: string, + newValue: boolean, + ) { if (!this.form) { return; } @@ -289,12 +321,12 @@ export const useForm = defineStore("form", { try { const response = await callUpdateFormBlock({ ...block, - is_disabled: newValue, + [property]: newValue, }); - const index = this.blocks?.findIndex((item) => { - return item.id === block.id; - }); + const index = this.blocks?.findIndex( + (item) => item.id === block.id, + ); if ( response.status === 200 && diff --git a/resources/js/stores/helpers/logic.ts b/resources/js/stores/helpers/logic.ts new file mode 100644 index 00000000..5692e261 --- /dev/null +++ b/resources/js/stores/helpers/logic.ts @@ -0,0 +1,149 @@ +export const operators: Array<{ key: Operator; label: string }> = [ + { key: "equals", label: "is equal to" }, + { key: "equalsNot", label: "is not equal to" }, + { key: "contains", label: "contains" }, + { key: "containsNot", label: "does not contain" }, + { key: "isLowerThan", label: "is lower than" }, + { key: "isGreaterThan", label: "is greater than" }, +]; + +export function evaluateCondition( + condition: FormBlockLogicCondition, + responseValue: any, +): boolean { + switch (condition.operator) { + case "equals": + return responseValue === condition.value; + case "equalsNot": + return responseValue !== condition.value; + case "contains": + return responseValue.includes(condition.value); + case "containsNot": + return !responseValue.includes(condition.value); + case "isLowerThan": + return responseValue < condition.value; + case "isGreaterThan": + return responseValue > condition.value; + default: + return false; + } +} + +export function getResponseValue(response: any): any { + return "payload" in response + ? response.payload + : response.map((p) => p.payload).join(", "); +} + +export function groupConditions( + evaluatedConditions: FormBlockLogicCondition[], +): FormBlockLogicCondition[][] { + const groups: FormBlockLogicCondition[][] = []; + let currentIndex = 0; + + evaluatedConditions.forEach((condition, index) => { + if (index === 0) { + groups[currentIndex] = [condition]; + } else { + if (condition.chainOperator === "or") { + currentIndex++; + } + if (!groups[currentIndex]) { + groups[currentIndex] = []; + } + groups[currentIndex].push(condition); + } + }); + + return groups; +} + +export function evaluateConditions( + conditions: FormBlockLogicCondition[], + responses: FormSubmitPayload, +): FormBlockLogicCondition[] { + return conditions.map((condition) => ({ + ...condition, + result: + condition.source && responses[condition.source] + ? evaluateCondition( + condition, + getResponseValue(responses[condition.source]), + ) + : false, + })); +} + +export function evaluateLogicRule( + logic: FormBlockLogic, + responses: FormSubmitPayload, +): boolean { + const evaluatedConditions = evaluateConditions( + logic.conditions as FormBlockLogicCondition[], + responses, + ); + const conditionGroups = groupConditions(evaluatedConditions); + + const finalResult = conditionGroups.some((group) => + group.every((condition) => condition.result), + ); + + switch (logic.action) { + case "show": + case "goto": + return finalResult; + case "hide": + return !finalResult; + } +} + +export function isBlockVisible( + block: PublicFormBlockModel, + responses: FormSubmitPayload, +): boolean { + if (!block.logics?.length) { + return true; + } + + const beforeLogics = block.logics.filter( + (logic) => + logic.evaluate === "before" || + logic.action === "show" || + logic.action === "hide", + ); + + // If any logic rule returns false, the block is not visible + return beforeLogics.every((logic) => evaluateLogicRule(logic, responses)); +} + +export function evaluateGotoLogic( + block: PublicFormBlockModel, + payload: FormSubmitPayload, +): { target: string } | null { + if (!block?.logics) return null; + + // Find goto actions that should be evaluated after block interaction + const gotoLogics = block.logics.filter( + (logic) => logic.action === "goto" && logic.evaluate === "after", + ); + + for (const logic of gotoLogics) { + const shouldExecute = evaluateLogicRule(logic, payload); + + if (shouldExecute && logic.action_payload) { + return { target: logic.action_payload }; + } + } + + return null; +} + +export function transformConditionForBackend( + condition: EditableFormBlockBlockLogicCondition, +): FormBlockLogicCondition { + return { + ...condition, + source: condition.source?.key, + operator: condition.operator.key, + }; +} diff --git a/resources/js/stores/helpers/queue.ts b/resources/js/stores/helpers/queue.ts new file mode 100644 index 00000000..c619ca4b --- /dev/null +++ b/resources/js/stores/helpers/queue.ts @@ -0,0 +1,63 @@ +export function createFlatQueue( + blocks: PublicFormBlockModel[], + parent_block: string | null = null, +): PublicFormBlockModel[] { + return blocks + .filter((block) => block.parent_block === parent_block) + .flatMap((block) => { + const queueItem = block; + if (block.type === "group") { + const children = blocks.filter( + (b) => b.parent_block === block.id, + ); + return [queueItem, ...createFlatQueue(children, block.id)]; + } + return [queueItem]; + }); +} + +type FindBlocksOptions = { + filterGroups?: boolean; + includeTarget?: boolean; +}; + +export function findAllBlocksBeforeTarget( + tree: TreeNode[], + targetId: string, + options: FindBlocksOptions = {}, +): FormBlockModel[] { + const { filterGroups = true, includeTarget = false } = options; + const result: FormBlockModel[] = []; + let found = false; + + function traverse(node: TreeNode) { + if (found) return; + + result.push(node.block); + + if (node.block.uuid === targetId) { + found = true; + return; + } + + for (const child of node.children) { + traverse(child); + if (found) break; + } + } + + for (const rootNode of tree) { + traverse(rootNode); + if (found) break; + } + + // Remove the target block if not included + if (found && !includeTarget) { + result.pop(); + } + + // Filter groups if needed + return filterGroups + ? result.filter((block) => block.type !== "group") + : result; +} diff --git a/resources/js/stores/index.ts b/resources/js/stores/index.ts index 4a7b5ac7..80bd615d 100644 --- a/resources/js/stores/index.ts +++ b/resources/js/stores/index.ts @@ -1,2 +1,3 @@ export * from "./form"; export * from "./workbench"; +export * from "./logic"; diff --git a/resources/js/stores/logic.ts b/resources/js/stores/logic.ts new file mode 100644 index 00000000..c78ae221 --- /dev/null +++ b/resources/js/stores/logic.ts @@ -0,0 +1,227 @@ +import { useForm } from "@/stores"; +import { defineStore } from "pinia"; +import { findAllBlocksBeforeTarget } from "./helpers/queue"; + +import { + callCreateFormBlockLogic, + callUpdateFormBlockLogic, + callDeleteFormBlockLogic, +} from "@/api/logics"; + +import { transformConditionForBackend } from "./helpers/logic"; + +interface LogicStore { + block: FormBlockModel | null; + validation: ValidationError[]; + isShowingLogicEditor: boolean; + hideRule: FormBlockLogic | null; + backup: FormBlockLogic[] | null; +} + +export const useLogic = defineStore("logic", { + state: (): LogicStore => { + return { + block: null, + isShowingLogicEditor: false, + hideRule: null, + validation: [], + backup: null, + }; + }, + + actions: { + showLogicEditor(block: FormBlockModel) { + this.block = block; + this.backupLogic(); + this.isShowingLogicEditor = true; + }, + + hideLogicEditor() { + this.block = null; + this.isShowingLogicEditor = false; + }, + + backupLogic() { + this.backup = this.block?.logics ?? null; + }, + + restoreLogic() { + if (!this.block) { + return; + } + + this.block.logics = this.backup ?? undefined; + }, + + addRule() { + if (!this.block) { + return; + } + + if (!this.block.logics) { + this.block.logics = []; + } + + this.block.logics.push({ + form_block_id: this.block.id, + name: "Rule #" + (this.block.logics.length + 1), + action: "hide", + action_payload: null, + evaluate: "before", + conditions: [], + }); + }, + + async removeRule(index: number) { + if (!this.block || !this.block.logics) { + return; + } + + if (this.block.logics[index].id === undefined) { + this.block.logics.splice(index, 1); + return; + } + + try { + const response = await callDeleteFormBlockLogic( + this.block.logics[index], + ); + + if (response.status === 200) { + this.block.logics.splice(index, 1); + } + } catch (error) { + console.warn(`Error deleting rule:`, error); + } + }, + + async saveBlockLogic() { + if (!this.block || !this.block.logics) { + return; + } + + // reset validation before each request + this.validation = []; + + const results = await Promise.allSettled( + this.block.logics.map((logic, index) => + this.saveSingleRule(logic).then((result) => ({ + result, + index, + })), + ), + ); + + const failedSaves = results + .map((result, index) => ({ ...result, originalIndex: index })) + .filter( + ( + result, + ): result is PromiseRejectedResult & { + originalIndex: number; + } => result.status === "rejected", + ); + + if (failedSaves.length > 0) { + failedSaves.forEach((failure) => { + this.validation[failure.originalIndex] = + failure.reason.response.data; + }); + } + + return { + failed: failedSaves.length, + totalRules: this.block.logics.length, + }; + }, + + async saveSingleRule(logic: FormBlockLogic) { + if (!this.block) { + throw new Error("Block is not defined"); + } + + try { + if (!logic.uuid) { + const response = await callCreateFormBlockLogic( + this.block.id, + logic, + ); + if (response.status === 201) { + return response.data; + } else { + throw new Error( + `Unexpected response status: ${response.status}`, + ); + } + } else { + const response = await callUpdateFormBlockLogic(logic); + + if (response.status === 200) { + return response.data; + } else { + throw new Error( + `Unexpected response status: ${response.status}`, + ); + } + } + } catch (error) { + console.warn(`Error saving rule:`, error); + throw error; + } + }, + + updateBlockLogic(logic: FormBlockLogic, index: number) { + if (!this.block) { + return; + } + + if (!this.block.logics) { + this.block.logics = []; + } + + const transformedConditions = logic.conditions.map((c) => + transformConditionForBackend(c), + ); + + logic.conditions = transformedConditions; + + this.block.logics[index] = logic; + }, + }, + + getters: { + allBlocks(): Array | null { + const formStore = useForm(); + + return formStore.blocks; + }, + + availableSourceBlocksWithTarget(state): Array { + const formStore = useForm(); + + if (!state.block?.uuid) { + return []; + } + + return findAllBlocksBeforeTarget( + formStore.blocksTree, + state.block.uuid, + { filterGroups: true, includeTarget: true }, + ); + }, + + availableSourceBlocks(state): Array { + const formStore = useForm(); + + if (!state.block?.uuid) { + return []; + } + + return findAllBlocksBeforeTarget( + formStore.blocksTree, + state.block.uuid, + { filterGroups: true, includeTarget: false }, + ); + }, + }, +}); diff --git a/resources/js/types/models.d.ts b/resources/js/types/models.d.ts index 58d7133f..ebdba58f 100644 --- a/resources/js/types/models.d.ts +++ b/resources/js/types/models.d.ts @@ -64,6 +64,50 @@ interface FormWebhookModel extends BaseModel { headers: Record | null; } +interface TreeNode { + block: FormBlockModel; + children: TreeNode[]; +} + +type Operator = + | "equals" + | "equalsNot" + | "contains" + | "containsNot" + | "isLowerThan" + | "isGreaterThan"; + +interface FormBlockLogicCondition { + source?: FormBlockModel["uuid"]; + operator?: Operator; + value: string; + chainOperator: "or" | "and"; + result?: boolean; +} + +interface EditableFormBlockBlockLogicCondition extends FormBlockLogicCondition { + source?: { key: FormBlockModel["uuid"] }; + operator: { key: Operator }; +} + +interface ValidationError { + message: string; + errors: Record; +} + +interface FormBlockLogic { + id?: number; + uuid?: string; + form_block_id: number; + name: string; + conditions: + | Array + | Array; + action: "show" | "hide" | "goto"; + action_payload: string | null; + evaluate: "before" | "after"; +} + interface FormBlockModel extends BaseModel { type: FormBlockType; message: string | null; @@ -77,6 +121,7 @@ interface FormBlockModel extends BaseModel { sequence: number; form_id: number; interactions: FormBlockInteractionModel[] | undefined; + logics: FormBlockLogic[] | undefined; } type FormBlockType = @@ -164,6 +209,7 @@ type PublicFormBlockModel = { parent_block: string | null; is_required: boolean | null; interactions: Array; + logics: Array | undefined; }; type PublicFormModel = { diff --git a/resources/js/utils/index.ts b/resources/js/utils/index.ts index 8436df2d..e4b70f61 100644 --- a/resources/js/utils/index.ts +++ b/resources/js/utils/index.ts @@ -67,3 +67,11 @@ export function replaceRouteQuery(query: Record): void { window.history.replaceState(historyState, "", url.toString()); } + +export function getTextFromHtml(html: string): string { + const div = document.createElement("div"); + div.innerHTML = html; + const text = div.textContent || div.innerText || ""; + + return text; +} diff --git a/resources/locales/de.json b/resources/locales/de.json index 90695e9c..ebbaf90a 100644 --- a/resources/locales/de.json +++ b/resources/locales/de.json @@ -1,42 +1,43 @@ { - "required": "Pflichtfeld", - "hints": { - "edit": "Drücke {0} um zu bearbeiten", - "confirm": "{0} um zu bestätigen" - }, - "validation": { - "consent_required": "Um fortzufahren, musst Du den Bedingungen zustimmen.", - "field_required": "Dieses Feld ist erforderlich.", - "valid_email": "Bitte gib eine gültige E-Mail-Adresse ein.", - "valid_link": "Bitte gib einen gültigen Link ein.", - "valid_number": "Bitte gib eine gültige Zahl ein.", - "valid_phone": "Bitte gib eine gültige Telefonnummer ein.", - "option_required": "Bitte wähle eine Option aus.", - "error": "Beim Validieren deiner Eingabe ist etwas schief gelaufen.", - "max_characters": "Du hast die maximale Anzahl von Zeichen überschritten.", - "no_file_interaction": "Es gibt keine gültige Konfiguration, um Dateien zu akzeptieren.", - "too_many_files": "Du hast zu viele Dateien hochgeladen. Du kannst bis zu {maxFiles} Dateien hochladen.", - "file_size": "Die Datei '{fileName}' überschreitet die maximale Größe von {maxFileSize} MB.", - "rating_required": "Bitte wählen Sie eine Bewertung aus." - }, - "admin": { - "blocks": "keine Blöcke | 1 Block | {n} Blöcke" - }, - "accept_submit": "Akzeptieren & Absenden", - "accept": "Akzeptieren", - "close": "Schliessen", - "type": "Gib deine Antwort ein", - "submit": "Absenden", - "privacy_policy": "Datenschutzerklärung", - "other": "Andere", - "next": "Weiter", - "legal_notice": "Impressum", - "page_previous": "Zur vorherigen Seite", - "page_next": "Zur nächsten Seite", - "form_submitted": "Formular abgeschickt", - "find_us": "Finde uns hier", - "enter": "Enter", - "continue": "Weiter", - "files_choose": "Wählen Sie Dateien aus", - "files_drop": "oder hier ablegen" + "required": "Pflichtfeld", + "hints": { + "edit": "Drücke {0} um zu bearbeiten", + "confirm": "{0} um zu bestätigen" + }, + "validation": { + "consent_required": "Um fortzufahren, musst Du den Bedingungen zustimmen.", + "field_required": "Dieses Feld ist erforderlich.", + "valid_email": "Bitte gib eine gültige E-Mail-Adresse ein.", + "valid_link": "Bitte gib einen gültigen Link ein.", + "valid_number": "Bitte gib eine gültige Zahl ein.", + "valid_phone": "Bitte gib eine gültige Telefonnummer ein.", + "option_required": "Bitte wähle eine Option aus.", + "error": "Beim Validieren deiner Eingabe ist etwas schief gelaufen.", + "max_characters": "Du hast die maximale Anzahl von Zeichen überschritten.", + "no_file_interaction": "Es gibt keine gültige Konfiguration, um Dateien zu akzeptieren.", + "too_many_files": "Du hast zu viele Dateien hochgeladen. Du kannst bis zu {maxFiles} Dateien hochladen.", + "file_size": "Die Datei '{fileName}' überschreitet die maximale Größe von {maxFileSize} MB.", + "rating_required": "Bitte wählen Sie eine Bewertung aus." + }, + "admin": { + "blocks": "keine Blöcke | 1 Block | {n} Blöcke", + "logicTitle": "keine Regeln | 1 Regel | {n} Regeln" + }, + "accept_submit": "Akzeptieren & Absenden", + "accept": "Akzeptieren", + "close": "Schliessen", + "type": "Gib deine Antwort ein", + "submit": "Absenden", + "privacy_policy": "Datenschutzerklärung", + "other": "Andere", + "next": "Weiter", + "legal_notice": "Impressum", + "page_previous": "Zur vorherigen Seite", + "page_next": "Zur nächsten Seite", + "form_submitted": "Formular abgeschickt", + "find_us": "Finde uns hier", + "enter": "Enter", + "continue": "Weiter", + "files_choose": "Wählen Sie Dateien aus", + "files_drop": "oder hier ablegen" } diff --git a/resources/locales/en.json b/resources/locales/en.json index 05c8add3..7e60944e 100644 --- a/resources/locales/en.json +++ b/resources/locales/en.json @@ -1,6 +1,7 @@ { "admin": { - "blocks": "no blocks | 1 block | {n} blocks" + "blocks": "no blocks | 1 block | {n} blocks", + "logicTitle": "no rules | 1 block rule | {n} block rules" }, "required": "required", "hints": { diff --git a/resources/locales/fr.json b/resources/locales/fr.json index a1ec8102..fa1a291f 100644 --- a/resources/locales/fr.json +++ b/resources/locales/fr.json @@ -1,42 +1,43 @@ { - "admin": { - "blocks": "pas de blocs | 1 bloc | {n} blocs" - }, - "required": "obligatoire", - "hints": { - "edit": "Appuyez sur {0} pour modifier", - "confirm": "{0} pour confirmer" - }, - "validation": { - "consent_required": "Pour continuer, vous devez accepter les termes et conditions.", - "field_required": "Ce champ est obligatoire.", - "valid_email": "Veuillez saisir un email valide.", - "valid_link": "Veuillez entrer un lien valide.", - "valid_number": "S'il vous plait, entrez un nombre valide.", - "valid_phone": "S'il vous plaît entrer un numéro de téléphone valide.", - "option_required": "Veuillez sélectionner une option.", - "error": "Une erreur s'est produite lors de la validation de votre saisie", - "max_characters": "Vous avez dépassé le nombre maximum de caractères autorisés.", - "no_file_interaction": "Il n'y a aucune configuration valide pour accepter les fichiers", - "too_many_files": "Vous avez téléchargé trop de fichiers. Vous pouvez télécharger jusqu'à {maxFiles} fichiers.", - "file_size": "Le fichier '{fileName}' dépasse la taille maximale de {maxFileSize} Mo.", - "rating_required": "Veuillez choisir une note" - }, - "accept_submit": "Accepter et soumettre", - "accept": "Accepter", - "close": "Fermer", - "type": "Tapez votre réponse", - "submit": "Soumettre", - "privacy_policy": "Politique de confidentialité", - "other": "Autre", - "next": "Suivant", - "legal_notice": "Mention légale", - "page_previous": "Aller à la page précédente", - "page_next": "Aller à la page suivante", - "form_submitted": "Formulaire soumis", - "find_us": "Retrouvez-nous ici", - "enter": "Entrer", - "continue": "Continuer", - "files_choose": "Choisir des fichiers", - "files_drop": "ou déposez ici" + "admin": { + "blocks": "pas de blocs | 1 bloc | {n} blocs", + "logicTitle": "pas de règles | 1 règle de blocage | {n} règles de blocage" + }, + "required": "obligatoire", + "hints": { + "edit": "Appuyez sur {0} pour modifier", + "confirm": "{0} pour confirmer" + }, + "validation": { + "consent_required": "Pour continuer, vous devez accepter les termes et conditions.", + "field_required": "Ce champ est obligatoire.", + "valid_email": "Veuillez saisir un email valide.", + "valid_link": "Veuillez entrer un lien valide.", + "valid_number": "S'il vous plait, entrez un nombre valide.", + "valid_phone": "S'il vous plaît entrer un numéro de téléphone valide.", + "option_required": "Veuillez sélectionner une option.", + "error": "Une erreur s'est produite lors de la validation de votre saisie", + "max_characters": "Vous avez dépassé le nombre maximum de caractères autorisés.", + "no_file_interaction": "Il n'y a aucune configuration valide pour accepter les fichiers", + "too_many_files": "Vous avez téléchargé trop de fichiers. Vous pouvez télécharger jusqu'à {maxFiles} fichiers.", + "file_size": "Le fichier '{fileName}' dépasse la taille maximale de {maxFileSize} Mo.", + "rating_required": "Veuillez choisir une note" + }, + "accept_submit": "Accepter et soumettre", + "accept": "Accepter", + "close": "Fermer", + "type": "Tapez votre réponse", + "submit": "Soumettre", + "privacy_policy": "Politique de confidentialité", + "other": "Autre", + "next": "Suivant", + "legal_notice": "Mention légale", + "page_previous": "Aller à la page précédente", + "page_next": "Aller à la page suivante", + "form_submitted": "Formulaire soumis", + "find_us": "Retrouvez-nous ici", + "enter": "Entrer", + "continue": "Continuer", + "files_choose": "Choisir des fichiers", + "files_drop": "ou déposez ici" } diff --git a/resources/locales/no.json b/resources/locales/no.json index f0c8deb7..8163097e 100644 --- a/resources/locales/no.json +++ b/resources/locales/no.json @@ -1,42 +1,43 @@ { - "admin": { - "blocks": "ingen blokker | 1 blokk | {n} blokker" - }, - "required": "påkrevd", - "hints": { - "edit": "Trykk {0} for å redigere", - "confirm": "{0} for å bekrefte" - }, - "validation": { - "consent_required": "For å fortsette, må du godta vilkårene.", - "field_required": "Dette feltet er påkrevd.", - "valid_email": "Vennligst skriv inn en gyldig e-postadresse.", - "valid_link": "Vennligst skriv inn en gyldig lenke.", - "valid_number": "Vennligst skriv inn et gyldig nummer.", - "valid_phone": "Vennligst skriv inn et gyldig telefonnummer.", - "option_required": "Vennligst velg et alternativ.", - "error": "Noe gikk galt under validering av inndataene dine", - "max_characters": "Du har overskredet det maksimale antallet tegn som er tillatt.", - "no_file_interaction": "Det er ingen gyldig konfigurasjon for å akseptere filer", - "too_many_files": "Du har lastet opp for mange filer. Du kan laste opp opptil {maxFiles} filer.", - "file_size": "Filen '{fileName}' overskrider maksimal størrelse på {maxFileSize} MB.", - "rating_required": "Vennligst velg en vurdering" - }, - "accept_submit": "Godkjenn og send inn", - "accept": "Godkjenn", - "close": "Lukk", - "type": "Skriv svaret ditt", - "submit": "Send inn", - "privacy_policy": "Personvern", - "other": "Annet", - "next": "Neste", - "legal_notice": "Juridisk merknad", - "page_previous": "Gå til forrige side", - "page_next": "Gå til neste side", - "form_submitted": "Skjema sendt inn", - "find_us": "Finn oss her", - "enter": "Legg inn", - "continue": "Fortsett", - "files_choose": "Velg filer", - "files_drop": "eller slipp her" + "admin": { + "blocks": "ingen blokker | 1 blokk | {n} blokker", + "logicTitle": "ingen regler | 1 blokkregel | {n} blokkeringsregler" + }, + "required": "påkrevd", + "hints": { + "edit": "Trykk {0} for å redigere", + "confirm": "{0} for å bekrefte" + }, + "validation": { + "consent_required": "For å fortsette, må du godta vilkårene.", + "field_required": "Dette feltet er påkrevd.", + "valid_email": "Vennligst skriv inn en gyldig e-postadresse.", + "valid_link": "Vennligst skriv inn en gyldig lenke.", + "valid_number": "Vennligst skriv inn et gyldig nummer.", + "valid_phone": "Vennligst skriv inn et gyldig telefonnummer.", + "option_required": "Vennligst velg et alternativ.", + "error": "Noe gikk galt under validering av inndataene dine", + "max_characters": "Du har overskredet det maksimale antallet tegn som er tillatt.", + "no_file_interaction": "Det er ingen gyldig konfigurasjon for å akseptere filer", + "too_many_files": "Du har lastet opp for mange filer. Du kan laste opp opptil {maxFiles} filer.", + "file_size": "Filen '{fileName}' overskrider maksimal størrelse på {maxFileSize} MB.", + "rating_required": "Vennligst velg en vurdering" + }, + "accept_submit": "Godkjenn og send inn", + "accept": "Godkjenn", + "close": "Lukk", + "type": "Skriv svaret ditt", + "submit": "Send inn", + "privacy_policy": "Personvern", + "other": "Annet", + "next": "Neste", + "legal_notice": "Juridisk merknad", + "page_previous": "Gå til forrige side", + "page_next": "Gå til neste side", + "form_submitted": "Skjema sendt inn", + "find_us": "Finn oss her", + "enter": "Legg inn", + "continue": "Fortsett", + "files_choose": "Velg filer", + "files_drop": "eller slipp her" } diff --git a/resources/locales/pl.json b/resources/locales/pl.json index 6c5f7a5c..cd53c6ec 100644 --- a/resources/locales/pl.json +++ b/resources/locales/pl.json @@ -1,42 +1,43 @@ { - "admin": { - "blocks": "brak bloków | 1 blok | {n} bloków" - }, - "required": "wymagane", - "hints": { - "edit": "Naciśnij {0} aby edytować", - "confirm": "{0} aby potwierdzić" - }, - "validation": { - "consent_required": "Aby kontynuować, musisz zaakceptować warunki.", - "field_required": "Tol pole jest wymagane.", - "valid_email": "Proszę wprowadzić prawidłowy adres e-mail.", - "valid_link": "Proszę wprowadzić prawidłowy link.", - "valid_number": "Proszę wprowadzić prawidłową liczbę.", - "valid_phone": "Proszę wprowadzić prawidłowy numer telefonu.", - "option_required": "Proszę wybrać opcję.", - "error": "Coś poszło nie tak podczas sprawdzania poprawności danych wejściowych", - "max_characters": "Przekroczyłeś maksymalną dozwoloną liczbę znaków.", - "no_file_interaction": "Nie ma poprawnej konfiguracji do akceptowania plików", - "too_many_files": "Przesłałeś zbyt wiele plików. Możesz przesłać maksymalnie {maxFiles} plików.", - "file_size": "Plik '{fileName}' przekracza maksymalny rozmiar {maxFileSize} MB.", - "rating_required": "Proszę wybrać ocenę." - }, - "accept_submit": "Zaakceptuj i wyślij", - "accept": "Akceptuj", - "close": "Zamknij", - "type": "Wprowadź swoją odpowiedź", - "submit": "Wyślij", - "privacy_policy": "Polityka Prywatności", - "other": "Inne", - "next": "Następny", - "legal_notice": "Informacje Prawne", - "page_previous": "Idź do poprzedniej Strony", - "page_next": "Idź do następnej Strony", - "form_submitted": "Formularz przesłany", - "find_us": "Znajdziesz nas tutaj", - "enter": "Enter", - "continue": "Kontynuuj", - "files_choose": "Wybierz pliki", - "files_drop": "lub upuść tutaj" + "admin": { + "blocks": "brak bloków | 1 blok | {n} bloków", + "logicTitle": "brak zasad | 1 reguła bloku | {n} reguły blokowania" + }, + "required": "wymagane", + "hints": { + "edit": "Naciśnij {0} aby edytować", + "confirm": "{0} aby potwierdzić" + }, + "validation": { + "consent_required": "Aby kontynuować, musisz zaakceptować warunki.", + "field_required": "Tol pole jest wymagane.", + "valid_email": "Proszę wprowadzić prawidłowy adres e-mail.", + "valid_link": "Proszę wprowadzić prawidłowy link.", + "valid_number": "Proszę wprowadzić prawidłową liczbę.", + "valid_phone": "Proszę wprowadzić prawidłowy numer telefonu.", + "option_required": "Proszę wybrać opcję.", + "error": "Coś poszło nie tak podczas sprawdzania poprawności danych wejściowych", + "max_characters": "Przekroczyłeś maksymalną dozwoloną liczbę znaków.", + "no_file_interaction": "Nie ma poprawnej konfiguracji do akceptowania plików", + "too_many_files": "Przesłałeś zbyt wiele plików. Możesz przesłać maksymalnie {maxFiles} plików.", + "file_size": "Plik '{fileName}' przekracza maksymalny rozmiar {maxFileSize} MB.", + "rating_required": "Proszę wybrać ocenę." + }, + "accept_submit": "Zaakceptuj i wyślij", + "accept": "Akceptuj", + "close": "Zamknij", + "type": "Wprowadź swoją odpowiedź", + "submit": "Wyślij", + "privacy_policy": "Polityka Prywatności", + "other": "Inne", + "next": "Następny", + "legal_notice": "Informacje Prawne", + "page_previous": "Idź do poprzedniej Strony", + "page_next": "Idź do następnej Strony", + "form_submitted": "Formularz przesłany", + "find_us": "Znajdziesz nas tutaj", + "enter": "Enter", + "continue": "Kontynuuj", + "files_choose": "Wybierz pliki", + "files_drop": "lub upuść tutaj" } diff --git a/resources/locales/sk.json b/resources/locales/sk.json index 210e1818..6ee19e54 100644 --- a/resources/locales/sk.json +++ b/resources/locales/sk.json @@ -1,42 +1,43 @@ { - "required": "Povinné", - "hints": { - "edit": "Stlač {0} pre úpravu", - "confirm": "{0} pre potvrdenie" - }, - "validation": { - "consent_required": "Pre pokračovanie je potrebný súhlas.", - "field_required": "Toto pole je povinné.", - "valid_email": "Prosíme, zadajte správnu e-mailovú adresu.", - "valid_link": "Prosíme, zadajte sprvány link.", - "valid_number": "Prosíme, zadajte správne číslo.", - "valid_phone": "Prosíme, zadajte správne telefónne číslo.", - "option_required": "Prosíme, vyberte nejakú možnosť.", - "error": "Niečo sa nepodarilo pri overení Vašich údajov", - "max_characters": "Prekročili ste maximálny povolený počet znakov.", - "no_file_interaction": "Neexistuje platná konfigurácia na prijímanie súborov.", - "too_many_files": "Nahrali ste príliš veľa súborov. Môžete nahrať až {maxFiles} súborov.", - "file_size": "Súbor '{fileName}' prekračuje maximálnu veľkosť {maxFileSize} MB.", - "rating_required": "Prosím, vyberte si hodnotenie." - }, - "admin": { - "blocks": "žiadne bloky | 1 blok | {n} blokov" - }, - "accept_submit": "Akceptovať a odoslať", - "accept": "Akceptovať", - "close": "Zavrieť", - "type": "Zadajte Vašu odpoveď", - "submit": "Odoslať", - "privacy_policy": "Ochrana osobných údajov", - "other": "Iné", - "next": "Ďalej", - "legal_notice": "Pravidlá používania", - "page_previous": "Na predošlú stránku", - "page_next": "Na ďalšiu stránku", - "form_submitted": "Formulár odoslaný", - "find_us": "Nájdete nás tu", - "enter": "Enter", - "continue": "Pokračovať", - "files_choose": "Vyberte súbory", - "files_drop": "alebo sem spustiť" + "required": "Povinné", + "hints": { + "edit": "Stlač {0} pre úpravu", + "confirm": "{0} pre potvrdenie" + }, + "validation": { + "consent_required": "Pre pokračovanie je potrebný súhlas.", + "field_required": "Toto pole je povinné.", + "valid_email": "Prosíme, zadajte správnu e-mailovú adresu.", + "valid_link": "Prosíme, zadajte sprvány link.", + "valid_number": "Prosíme, zadajte správne číslo.", + "valid_phone": "Prosíme, zadajte správne telefónne číslo.", + "option_required": "Prosíme, vyberte nejakú možnosť.", + "error": "Niečo sa nepodarilo pri overení Vašich údajov", + "max_characters": "Prekročili ste maximálny povolený počet znakov.", + "no_file_interaction": "Neexistuje platná konfigurácia na prijímanie súborov.", + "too_many_files": "Nahrali ste príliš veľa súborov. Môžete nahrať až {maxFiles} súborov.", + "file_size": "Súbor '{fileName}' prekračuje maximálnu veľkosť {maxFileSize} MB.", + "rating_required": "Prosím, vyberte si hodnotenie." + }, + "admin": { + "blocks": "žiadne bloky | 1 blok | {n} blokov", + "logicTitle": "žiadne pravidlá | pravidlo 1 bloku | {n} pravidiel blokovania" + }, + "accept_submit": "Akceptovať a odoslať", + "accept": "Akceptovať", + "close": "Zavrieť", + "type": "Zadajte Vašu odpoveď", + "submit": "Odoslať", + "privacy_policy": "Ochrana osobných údajov", + "other": "Iné", + "next": "Ďalej", + "legal_notice": "Pravidlá používania", + "page_previous": "Na predošlú stránku", + "page_next": "Na ďalšiu stránku", + "form_submitted": "Formulár odoslaný", + "find_us": "Nájdete nás tu", + "enter": "Enter", + "continue": "Pokračovať", + "files_choose": "Vyberte súbory", + "files_drop": "alebo sem spustiť" } diff --git a/resources/locales/uk.json b/resources/locales/uk.json index 29da7384..53493121 100644 --- a/resources/locales/uk.json +++ b/resources/locales/uk.json @@ -1,42 +1,43 @@ { - "admin": { - "blocks": "немає блоків | 1 блок | {n} блоків" - }, - "required": "необхідно", - "hints": { - "edit": "Натисніть {0} щоб відредагувати", - "confirm": "{0} щоб підтвердити" - }, - "validation": { - "consent_required": "Щоб продовжити, ви повинні погодитися з умовами та положеннями.", - "field_required": "Це поле обов'язкове для заповнення.", - "valid_email": "Будь ласка, введіть правильну email адресу.", - "valid_link": "Будь ласка, введіть правильне посилання.", - "valid_number": "Будь ласка, введіть правильний номер.", - "valid_phone": "Будь ласка, введіть правильний номер телефона.", - "option_required": "Будь ласка, оберіть варіант.", - "error": "Щось пішло не так під час перевірки ваших даних", - "max_characters": "Ви перевищили максимально допустиму кількість символів.", - "no_file_interaction": "Немає дійсної конфігурації для прийняття файлів", - "too_many_files": "Ви завантажили забагато файлів. Ви можете завантажити до {maxFiles} файлів.", - "file_size": "Файл '{fileName}' перевищує максимальний розмір {maxFileSize} МБ.", - "rating_required": "Будь ласка, оберіть рейтинг." - }, - "accept_submit": "Прийняти та відправити", - "accept": "Прийняти", - "close": "Закрити", - "type": "Введіть свою відповідь", - "submit": "Надіслати", - "privacy_policy": "Політика конфіденційності", - "other": "Інше", - "next": "Наступне", - "legal_notice": "Юридичне повідомлення", - "page_previous": "Повернутись на минулу сторінку", - "page_next": "Перейти на наступну сторінку", - "form_submitted": "Форма надіслана", - "find_us": "Знайдіть нас тут", - "enter": "Ввести", - "continue": "Продовжуйте", - "files_choose": "Виберіть файли", - "files_drop": "або впустіть сюди" + "admin": { + "blocks": "немає блоків | 1 блок | {n} блоків", + "logicTitle": "немає правил | 1 правило блоку | {n} правил блокування" + }, + "required": "необхідно", + "hints": { + "edit": "Натисніть {0} щоб відредагувати", + "confirm": "{0} щоб підтвердити" + }, + "validation": { + "consent_required": "Щоб продовжити, ви повинні погодитися з умовами та положеннями.", + "field_required": "Це поле обов'язкове для заповнення.", + "valid_email": "Будь ласка, введіть правильну email адресу.", + "valid_link": "Будь ласка, введіть правильне посилання.", + "valid_number": "Будь ласка, введіть правильний номер.", + "valid_phone": "Будь ласка, введіть правильний номер телефона.", + "option_required": "Будь ласка, оберіть варіант.", + "error": "Щось пішло не так під час перевірки ваших даних", + "max_characters": "Ви перевищили максимально допустиму кількість символів.", + "no_file_interaction": "Немає дійсної конфігурації для прийняття файлів", + "too_many_files": "Ви завантажили забагато файлів. Ви можете завантажити до {maxFiles} файлів.", + "file_size": "Файл '{fileName}' перевищує максимальний розмір {maxFileSize} МБ.", + "rating_required": "Будь ласка, оберіть рейтинг." + }, + "accept_submit": "Прийняти та відправити", + "accept": "Прийняти", + "close": "Закрити", + "type": "Введіть свою відповідь", + "submit": "Надіслати", + "privacy_policy": "Політика конфіденційності", + "other": "Інше", + "next": "Наступне", + "legal_notice": "Юридичне повідомлення", + "page_previous": "Повернутись на минулу сторінку", + "page_next": "Перейти на наступну сторінку", + "form_submitted": "Форма надіслана", + "find_us": "Знайдіть нас тут", + "enter": "Ввести", + "continue": "Продовжуйте", + "files_choose": "Виберіть файли", + "files_drop": "або впустіть сюди" } diff --git a/resources/locales/zh.json b/resources/locales/zh.json index 047dd7d4..849710d1 100644 --- a/resources/locales/zh.json +++ b/resources/locales/zh.json @@ -1,42 +1,43 @@ { - "admin": { - "blocks": "没有内容块 | 1 内容块 | {n} 内容块" - }, - "required": "必填", - "hints": { - "edit": "按 {0} 进行编辑", - "confirm": "按 {0} 确认" - }, - "validation": { - "consent_required": "继续操作前,您必须同意条款和条件。", - "field_required": "此字段为必填项。", - "valid_email": "请输入有效的邮箱。", - "valid_link": "请输入有效的链接。", - "valid_number": "请输入有效的数字。", - "valid_phone": "请输入有效的电话号码。", - "option_required": "请选择一个选项。", - "error": "验证您的输入时出错", - "max_characters": "您已超过允许的最大字符数。", - "no_file_interaction": "没有有效的配置来接受文件", - "too_many_files": "您已上传了太多文件。您最多可以上传 {maxFiles} 个文件。", - "file_size": "文件“{fileName}”超过了 {maxFileSize} MB 的最大大小。", - "rating_required": "请您选择一个评分" - }, - "accept_submit": "接受并提交", - "accept": "接受", - "close": "关闭", - "type": "输入您的答案", - "submit": "提交", - "privacy_policy": "隐私政策", - "other": "其他", - "next": "下一步", - "legal_notice": "法律声明", - "page_previous": "返回上一页", - "page_next": "前往下一页", - "form_submitted": "表单已提交", - "find_us": "在这里找到我们", - "enter": "输入", - "continue": "继续", - "files_choose": "选择文件", - "files_drop": "或者拖放到这里" + "admin": { + "blocks": "没有内容块 | 1 内容块 | {n} 内容块", + "logicTitle": "没有规则| 1 块规则 | {n} 块规则" + }, + "required": "必填", + "hints": { + "edit": "按 {0} 进行编辑", + "confirm": "按 {0} 确认" + }, + "validation": { + "consent_required": "继续操作前,您必须同意条款和条件。", + "field_required": "此字段为必填项。", + "valid_email": "请输入有效的邮箱。", + "valid_link": "请输入有效的链接。", + "valid_number": "请输入有效的数字。", + "valid_phone": "请输入有效的电话号码。", + "option_required": "请选择一个选项。", + "error": "验证您的输入时出错", + "max_characters": "您已超过允许的最大字符数。", + "no_file_interaction": "没有有效的配置来接受文件", + "too_many_files": "您已上传了太多文件。您最多可以上传 {maxFiles} 个文件。", + "file_size": "文件“{fileName}”超过了 {maxFileSize} MB 的最大大小。", + "rating_required": "请您选择一个评分" + }, + "accept_submit": "接受并提交", + "accept": "接受", + "close": "关闭", + "type": "输入您的答案", + "submit": "提交", + "privacy_policy": "隐私政策", + "other": "其他", + "next": "下一步", + "legal_notice": "法律声明", + "page_previous": "返回上一页", + "page_next": "前往下一页", + "form_submitted": "表单已提交", + "find_us": "在这里找到我们", + "enter": "输入", + "continue": "继续", + "files_choose": "选择文件", + "files_drop": "或者拖放到这里" } diff --git a/routes/api.php b/routes/api.php index 06906820..3baf052e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,31 +1,32 @@ post('interactions/{interaction}', [FormBlockInteractionController::class, 'update'])->name('api.interactions.update'); $router->delete('interactions/{interaction}', [FormBlockInteractionController::class, 'delete'])->name('api.interactions.delete'); + // Logic API Routes + $router->post('{block}/logic', [FormBlockLogicController::class, 'create'])->name('api.logics.create'); + $router->post('logic/{logic}', [FormBlockLogicController::class, 'update'])->name('api.logics.update'); + $router->delete('logic/{logic}', [FormBlockLogicController::class, 'delete'])->name('api.logics.delete'); + // Sequence Routes $router->post('{form}/blocks/sequence', FormBlockSequenceController::class)->name('api.blocks.sequence'); $router->post('{block}/interactions/sequence', FormBlockInteractionSequenceController::class)->name('api.interactions.sequence'); diff --git a/tests/Feature/ChangeSequenceOfInteractionsTest.php b/tests/Feature/ChangeSequenceOfInteractionsTest.php index a8c47e57..0507c1d1 100644 --- a/tests/Feature/ChangeSequenceOfInteractionsTest.php +++ b/tests/Feature/ChangeSequenceOfInteractionsTest.php @@ -6,7 +6,7 @@ uses(RefreshDatabase::class); -test('each_block_has_a_sequence', function () { +test('each block interaction has a sequence', function () { $block = FormBlock::factory()->create(); $interactions = FormBlockInteraction::factory()->count(3)->create([ 'form_block_id' => $block->id, @@ -17,7 +17,7 @@ $this->assertEquals(2, $interactions[2]->sequence); }); -test('can_update_the_block_interactions_sequence', function () { +test('can update the block interactions sequence', function () { $block = FormBlock::factory()->create(); $interactions = FormBlockInteraction::factory()->count(3)->create([ 'form_block_id' => $block->id, @@ -41,7 +41,7 @@ $this->assertEquals(2, $interactions[1]->sequence); }); -test('deleting_an_interaction_updates_sequence_on_remaining_interactions', function () { +test('deleting an interaction updates sequence on remaining interactions', function () { $this->withoutExceptionHandling(); $block = FormBlock::factory()->create(); diff --git a/tests/Feature/FormBlockLogicTest.php b/tests/Feature/FormBlockLogicTest.php new file mode 100644 index 00000000..9bc7479b --- /dev/null +++ b/tests/Feature/FormBlockLogicTest.php @@ -0,0 +1,68 @@ +create(); + $block = FormBlock::factory()->for($firstBlock->form)->create(); + + $response = $this->actingAs($block->form->user) + ->json('post', route('api.logics.create', $block->id), [ + 'name' => 'Test Rule', + 'conditions' => [ + [ + 'source' => $firstBlock->uuid, + 'operator' => 'equals', + 'value' => 'test', + 'chainOperator' => 'and', + ] + ], + 'action' => 'hide', + 'evaluate' => 'before', + ]); + + $response->assertStatus(201); +}); + +test('can update an existing form block logic', function () { + $logic = FormBlockLogic::factory()->create(); + $block = FormBlock::factory()->for($logic->formBlock->form)->create(); + + $this->actingAs($logic->formBlock->form->user) + ->json('post', route('api.logics.update', $logic->id), [ + 'name' => 'Test Rule', + 'conditions' => [ + [ + 'source' => $block->uuid, + 'operator' => 'equals', + 'value' => 'test', + 'chainOperator' => 'and', + ] + ], + 'action' => 'hide', + 'evaluate' => 'before', + ])->assertStatus(200); + + $logic->refresh(); + + $this->assertEquals('Test Rule', $logic->name); + $this->assertEquals('hide', $logic->action); + $this->assertEquals('before', $logic->evaluate); + $this->assertCount(1, $logic->conditions); + $this->assertEquals($block->uuid, $logic->conditions[0]['source']); +}); + + +test('can delete a logic rule', function () { + $logic = FormBlockLogic::factory()->create(); + + $this->actingAs($logic->formBlock->form->user) + ->json('DELETE', route('api.logics.delete', $logic->id)) + ->assertStatus(200); + + $this->assertNull($logic->fresh()); +}); diff --git a/tests/Feature/FormStoryboardTest.php b/tests/Feature/FormStoryboardTest.php index 23745acd..8a788b81 100644 --- a/tests/Feature/FormStoryboardTest.php +++ b/tests/Feature/FormStoryboardTest.php @@ -1,8 +1,9 @@ json('count'))->toBe(0); }); - test('interactions in a storyboard are sorted by sequence', function () { $form = Form::factory()->create(); @@ -189,3 +189,15 @@ $distinctOrders = collect($results)->unique(); $this->assertTrue($distinctOrders->count() > 1, 'The interactions are not randomized'); }); + +test('a block contains logic definitions if they exist', function () { + $blockA = FormBlock::factory()->create(); + $logic = FormBlockLogic::factory()->for($blockA)->create(); + + $blockB = FormBlock::factory()->for($blockA->form)->create(); + + $response = $this->json('get', route('api.public.forms.storyboard', $blockA->form->uuid)); + + $this->assertCount(1, $response->json('blocks.0.logics')); + $this->assertCount(0, $response->json('blocks.1.logics')); +}); diff --git a/tests/Feature/ManageBlocksTest.php b/tests/Feature/ManageBlocksTest.php index 276d4a93..b82531c7 100644 --- a/tests/Feature/ManageBlocksTest.php +++ b/tests/Feature/ManageBlocksTest.php @@ -1,18 +1,19 @@ create(); $response = $this->actingAs($form->user) @@ -27,7 +28,7 @@ $this->assertEquals($block->type, FormBlockType::none); }); -test('can_create_a_new_block_of_type_group', function () { +test('can create a new block of type group', function () { $form = Form::factory()->create(); $response = $this->actingAs($form->user) @@ -44,7 +45,7 @@ $this->assertEquals($block->type, FormBlockType::group); }); -test('can_only_create_blocks_for_forms_that_a_user_owns', function () { +test('can only create blocks for forms that a user owns', function () { /** @var User $otherUser */ $otherUser = User::factory()->withTeam()->create([]); $form = Form::factory()->create(); @@ -55,7 +56,7 @@ }); -test('can_get_blocks_related_to_a_form', function () { +test('can get blocks related to a form', function () { $form = Form::factory()->create(); FormBlock::factory()->count(5)->create([ @@ -69,7 +70,21 @@ $this->assertCount(5, $response->json()); }); -test('can_get_blocks_with_results_loaded', function () { +test('can get a block with logic', function () { + $form = Form::factory()->create(); + $block = FormBlock::factory()->for($form)->create(); + $logic = FormBlockLogic::factory()->create([ + 'form_block_id' => $block->id, + ]); + + $response = $this->actingAs($form->user) + ->json('get', route('api.blocks.index', $form->uuid)); + + $response->assertStatus(200); + $this->assertCount(1, $response->json('0.logics')); +}); + +test('can get blocks with results loaded', function () { $this->seed(SimpleFormSeeder::class); $form = Form::first(); @@ -85,7 +100,7 @@ $this->assertNotNull($response->json('2.interactions.0.responses_count')); }); -test('can_update_existing_blocks', function () { +test('can update existing blocks', function () { $block = FormBlock::factory()->create([ 'message' => 'Hey there', 'type' => FormBlockType::none, @@ -101,7 +116,7 @@ $this->assertEquals(FormBlockType::radio->value, $response->json('type')); }); -test('can_update_a_block_with_a_empty_message', function () { +test('can update a block with a empty message', function () { $block = FormBlock::factory()->create([ 'message' => 'Test Message', 'type' => FormBlockType::none, @@ -117,7 +132,7 @@ $this->assertEquals(FormBlockType::radio->value, $response->json('type')); }); -test('cannot_create_or_update_blocks_of_not_owned_form', function () { +test('cannot create or update blocks of not owned form', function () { /** @var User $otherUser */ $otherUser = User::factory()->withTeam()->create(); @@ -141,7 +156,7 @@ $this->assertEquals(FormBlockType::none, $block->fresh()->type); }); -test('when_blocks_are_updated_an_event_is_fired', function () { +test('when blocks are updated an event is fired', function () { Event::fake(); $block = FormBlock::factory()->create([ @@ -160,7 +175,7 @@ }); }); -test('can_delete_a_block', function () { +test('can delete a block', function () { $block = FormBlock::factory()->create(); $this->actingAs($block->form->user) @@ -170,7 +185,7 @@ $this->assertNull($block->fresh()); }); -test('cannot_delete_blocks_of_other_users', function () { +test('cannot delete blocks of other users', function () { /** @var User $otherUser */ $otherUser = User::factory()->withTeam()->create(); $block = FormBlock::factory()->create(); @@ -182,7 +197,7 @@ $this->assertNotNull($block->fresh()); }); -test('can_set_a_title_for_the_results_view', function () { +test('can set a title for the results view', function () { $block = FormBlock::factory()->create(); $this->actingAs($block->form->user) @@ -193,7 +208,7 @@ $this->assertEquals('This is a block title', $block->fresh()->title); }); -test('can_remove_a_title_for_the_results_view', function () { +test('can remove a title for the results view', function () { $block = FormBlock::factory()->create([ 'title' => 'Title is set', ]); @@ -204,7 +219,7 @@ $this->assertNull($block->fresh()->title); }); -test('can_make_a_block_required_for_form_user', function () { +test('can make a block required for form user', function () { $block = FormBlock::factory()->create(); $this->actingAs($block->form->user) @@ -218,7 +233,7 @@ $this->assertFalse($block->fresh()->is_required); }); -test('can_save_information_in_an_options_field', function () { +test('can save information in an options field', function () { $block = FormBlock::factory()->create(); $this->actingAs($block->form->user)