Compare commits
10 Commits
a030a84652
...
3a8e601a37
| Author | SHA1 | Date |
|---|---|---|
|
|
3a8e601a37 | |
|
|
a11fb01bef | |
|
|
a140f789c2 | |
|
|
9a42aacc3a | |
|
|
b47a528075 | |
|
|
cac88e7a3c | |
|
|
360b116062 | |
|
|
72a87f1de8 | |
|
|
61fc30b95c | |
|
|
f3a02df80e |
|
|
@ -60,6 +60,7 @@ jobs:
|
||||||
build-args: |
|
build-args: |
|
||||||
APP_ENV=${{ env.VERSION == 'dev' && 'local' || 'production' }}
|
APP_ENV=${{ env.VERSION == 'dev' && 'local' || 'production' }}
|
||||||
COMPOSER_FLAGS=${{ env.VERSION == 'dev' && '--optimize-autoloader --no-interaction' || '--no-dev --optimize-autoloader --no-interaction' }}
|
COMPOSER_FLAGS=${{ env.VERSION == 'dev' && '--optimize-autoloader --no-interaction' || '--no-dev --optimize-autoloader --no-interaction' }}
|
||||||
|
APP_VERSION=${{ env.VERSION }}
|
||||||
tags: ${{ env.API_TAGS }}
|
tags: ${{ env.API_TAGS }}
|
||||||
cache-from: type=registry,ref=${{secrets.DOCKER_API_REPO}}:dev
|
cache-from: type=registry,ref=${{secrets.DOCKER_API_REPO}}:dev
|
||||||
cache-to: type=inline
|
cache-to: type=inline
|
||||||
|
|
@ -71,6 +72,8 @@ jobs:
|
||||||
file: docker/Dockerfile.client
|
file: docker/Dockerfile.client
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
|
build-args: |
|
||||||
|
APP_VERSION=${{ env.VERSION }}
|
||||||
tags: ${{ env.UI_TAGS }}
|
tags: ${{ env.UI_TAGS }}
|
||||||
cache-from: type=registry,ref=${{secrets.DOCKER_UI_REPO}}:dev
|
cache-from: type=registry,ref=${{secrets.DOCKER_UI_REPO}}:dev
|
||||||
cache-to: type=inline
|
cache-to: type=inline
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
# OpnForm Nginx Setup Guide
|
||||||
|
|
||||||
|
This guide explains how to set up OpnForm with a host-level nginx configuration.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
The modified setup removes the main nginx ingress container and exposes services directly:
|
||||||
|
|
||||||
|
- **UI Service**: Exposed on port 7655 (HTTP)
|
||||||
|
- **API Service**: Exposed on port 7654 (HTTP via minimal nginx container)
|
||||||
|
- **Database**: PostgreSQL (internal only)
|
||||||
|
- **Redis**: Cache service (internal only)
|
||||||
|
|
||||||
|
## Key Changes from Default Setup
|
||||||
|
|
||||||
|
1. **Removed YAML anchors** - Each container now has its own explicit configuration to avoid conflicts
|
||||||
|
2. **Removed main ingress container** - Your host nginx handles all routing
|
||||||
|
3. **Added minimal api-nginx** - Small nginx container just to convert FastCGI to HTTP for the API
|
||||||
|
4. **Custom ports** - Using 7654-7655 range to avoid conflicts
|
||||||
|
|
||||||
|
## Setup Steps
|
||||||
|
|
||||||
|
### 1. Stop any existing containers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
docker compose -f docker-compose.dev.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Run the setup script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/docker-setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Verify services are running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see:
|
||||||
|
- opnform-api (healthy)
|
||||||
|
- opnform-api-nginx (healthy)
|
||||||
|
- opnform-api-worker (running)
|
||||||
|
- opnform-api-scheduler (running)
|
||||||
|
- opnform-client (healthy)
|
||||||
|
- opnform-redis (healthy)
|
||||||
|
- opnform-db (healthy)
|
||||||
|
|
||||||
|
### 4. Configure your host nginx
|
||||||
|
|
||||||
|
Copy the example configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp nginx-host-example.conf /etc/nginx/sites-available/forms.portnimara.dev
|
||||||
|
sudo ln -s /etc/nginx/sites-available/forms.portnimara.dev /etc/nginx/sites-enabled/
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit the file to adjust:
|
||||||
|
- SSL certificate paths
|
||||||
|
- Server name if different
|
||||||
|
- Any other site-specific settings
|
||||||
|
|
||||||
|
### 5. Test nginx configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nginx -t
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Reload nginx
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Port already in use
|
||||||
|
|
||||||
|
If you get "port already allocated" errors:
|
||||||
|
|
||||||
|
1. Check what's using the ports:
|
||||||
|
```bash
|
||||||
|
sudo lsof -i :7654
|
||||||
|
sudo lsof -i :7655
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Stop conflicting services or change the ports in docker-compose.yml
|
||||||
|
|
||||||
|
### API not responding
|
||||||
|
|
||||||
|
1. Check the api-nginx logs:
|
||||||
|
```bash
|
||||||
|
docker logs opnform-api-nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify the API container is running:
|
||||||
|
```bash
|
||||||
|
docker logs opnform-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### UI not loading
|
||||||
|
|
||||||
|
1. Check the client logs:
|
||||||
|
```bash
|
||||||
|
docker logs opnform-client
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Ensure the client/.env file has correct API URL settings
|
||||||
|
|
||||||
|
## Port Reference
|
||||||
|
|
||||||
|
- **7654**: API (HTTP) - proxied through api-nginx to PHP-FPM
|
||||||
|
- **7655**: UI (HTTP) - Nuxt.js frontend
|
||||||
|
- **9000**: PHP-FPM (internal only, FastCGI protocol)
|
||||||
|
- **5432**: PostgreSQL (internal only)
|
||||||
|
- **6379**: Redis (internal only)
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
1. Ports are bound to 127.0.0.1 only, not exposed to external network
|
||||||
|
2. All traffic should go through your host nginx with SSL
|
||||||
|
3. The minimal api-nginx container only handles FastCGI conversion, no SSL termination
|
||||||
|
|
||||||
|
## Default Credentials
|
||||||
|
|
||||||
|
- Email: admin@opnform.com
|
||||||
|
- Password: password
|
||||||
|
|
||||||
|
**Important**: Change these immediately after first login!
|
||||||
|
|
@ -14,6 +14,7 @@ class FeatureFlagsController extends Controller
|
||||||
'self_hosted' => config('app.self_hosted', true),
|
'self_hosted' => config('app.self_hosted', true),
|
||||||
'custom_domains' => config('custom-domains.enabled', false),
|
'custom_domains' => config('custom-domains.enabled', false),
|
||||||
'ai_features' => !empty(config('services.openai.api_key')),
|
'ai_features' => !empty(config('services.openai.api_key')),
|
||||||
|
'version' => $this->getAppVersion(),
|
||||||
|
|
||||||
'billing' => [
|
'billing' => [
|
||||||
'enabled' => !empty(config('cashier.key')) && !empty(config('cashier.secret')),
|
'enabled' => !empty(config('cashier.key')) && !empty(config('cashier.secret')),
|
||||||
|
|
@ -44,4 +45,17 @@ class FeatureFlagsController extends Controller
|
||||||
|
|
||||||
return response()->json($featureFlags);
|
return response()->json($featureFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the application version from Docker environment or fallback
|
||||||
|
*/
|
||||||
|
private function getAppVersion(): ?string
|
||||||
|
{
|
||||||
|
// Only return version for self-hosted installations
|
||||||
|
if (!config('app.self_hosted', true)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config('app.docker_version');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,9 +179,9 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Standard field processing (text, ID generation, etc.)
|
// Standard field processing (text, ID generation, etc.)
|
||||||
if ($field['type'] == 'text' && isset($field['generates_uuid']) && $field['generates_uuid']) {
|
if ((!$answerValue || !Str::isUuid($answerValue)) && $field['type'] == 'text' && isset($field['generates_uuid']) && $field['generates_uuid']) {
|
||||||
$finalData[$field['id']] = ($this->form->is_pro) ? Str::uuid()->toString() : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
$finalData[$field['id']] = ($this->form->is_pro) ? Str::uuid()->toString() : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
||||||
} elseif ($field['type'] == 'text' && isset($field['generates_auto_increment_id']) && $field['generates_auto_increment_id']) {
|
} elseif ((!$answerValue || !is_int($answerValue)) && $field['type'] == 'text' && isset($field['generates_auto_increment_id']) && $field['generates_auto_increment_id']) {
|
||||||
$finalData[$field['id']] = ($this->form->is_pro) ? (string) ($this->form->submissions_count + 1) : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
$finalData[$field['id']] = ($this->form->is_pro) ? (string) ($this->form->submissions_count + 1) : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
||||||
} else {
|
} else {
|
||||||
$finalData[$field['id']] = $answerValue;
|
$finalData[$field['id']] = $answerValue;
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,14 @@ class IntegrationLogicRule implements DataAwareRule, ValidationRule
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$typeField = $condition['value']['property_meta']['type'];
|
||||||
|
$operator = $condition['value']['operator'];
|
||||||
|
|
||||||
|
// If operator has no format and no expected_type, it means it doesn't need input
|
||||||
|
if (!isset(FormPropertyLogicRule::getConditionMapping()[$typeField]['comparators'][$operator]['expected_type'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($condition['value']['value'])) {
|
if (!isset($condition['value']['value'])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
$this->conditionErrors[] = 'missing condition value';
|
$this->conditionErrors[] = 'missing condition value';
|
||||||
|
|
@ -54,8 +62,6 @@ class IntegrationLogicRule implements DataAwareRule, ValidationRule
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$typeField = $condition['value']['property_meta']['type'];
|
|
||||||
$operator = $condition['value']['operator'];
|
|
||||||
$value = $condition['value']['value'];
|
$value = $condition['value']['value'];
|
||||||
|
|
||||||
if (!isset(FormPropertyLogicRule::getConditionMapping()[$typeField])) {
|
if (!isset(FormPropertyLogicRule::getConditionMapping()[$typeField])) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,17 @@ return [
|
||||||
|
|
||||||
'name' => env('APP_NAME', 'OpnForm'),
|
'name' => env('APP_NAME', 'OpnForm'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Version
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value is the version of your application. Used for display purposes
|
||||||
|
| and fallback when Docker build version is not available.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
'docker_version' => env('APP_VERSION_DOCKER'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Application Environment
|
| Application Environment
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,58 @@ it('can CRUD form integration', function () {
|
||||||
'message' => 'Form Integration was deleted.'
|
'message' => 'Form Integration was deleted.'
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can create form integration with checkbox logic', function () {
|
||||||
|
$user = $this->actingAsProUser();
|
||||||
|
$workspace = $this->createUserWorkspace($user);
|
||||||
|
$form = $this->createForm($user, $workspace, [
|
||||||
|
'properties' => [
|
||||||
|
[
|
||||||
|
'id' => 'checkbox_field',
|
||||||
|
'name' => 'Checkbox Field',
|
||||||
|
'type' => 'checkbox'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'text_field',
|
||||||
|
'name' => 'Text Field',
|
||||||
|
'type' => 'text',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'status' => true,
|
||||||
|
'integration_id' => 'email',
|
||||||
|
'logic' => [
|
||||||
|
'operatorIdentifier' => 'and',
|
||||||
|
'children' => [
|
||||||
|
[
|
||||||
|
'identifier' => 'checkbox_field',
|
||||||
|
'value' => [
|
||||||
|
'operator' => 'is_checked',
|
||||||
|
'property_meta' => [
|
||||||
|
'id' => 'checkbox_field',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'settings' => [
|
||||||
|
'send_to' => 'test@test.com',
|
||||||
|
'sender_name' => 'OpnForm',
|
||||||
|
'subject' => 'New form submission with checkbox logic',
|
||||||
|
'email_content' => 'Checkbox logic triggered.',
|
||||||
|
'include_submission_data' => true,
|
||||||
|
'include_hidden_fields_submission_data' => false,
|
||||||
|
'reply_to' => null
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->postJson(route('open.forms.integration.create', $form->id), $data)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertJson([
|
||||||
|
'type' => 'success',
|
||||||
|
'message' => 'Form Integration was created.'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
it('can update form with existing record', function () {
|
it('can update form with existing record', function () {
|
||||||
$user = $this->actingAsProUser();
|
$user = $this->actingAsProUser();
|
||||||
|
|
@ -39,3 +39,56 @@ it('can update form with existing record', function () {
|
||||||
expect($response->json('data.' . $nameProperty['id']))->toBe('Testing Updated');
|
expect($response->json('data.' . $nameProperty['id']))->toBe('Testing Updated');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can update form with existing record but generates_uuid field is not update', function () {
|
||||||
|
$user = $this->actingAsProUser();
|
||||||
|
$workspace = $this->createUserWorkspace($user);
|
||||||
|
$form = $this->createForm($user, $workspace, [
|
||||||
|
'editable_submissions' => true,
|
||||||
|
'properties' => [
|
||||||
|
[
|
||||||
|
'id' => 'uuid_field',
|
||||||
|
'type' => 'text',
|
||||||
|
'generates_uuid' => true,
|
||||||
|
'name' => 'UUID Field'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'name',
|
||||||
|
'type' => 'text',
|
||||||
|
'name' => 'Name'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->postJson(route('forms.answer', $form->slug), ['name' => 'Testing', 'uuid_field' => null])
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertJson([
|
||||||
|
'type' => 'success',
|
||||||
|
'message' => 'Form submission saved.',
|
||||||
|
]);
|
||||||
|
$submissionId = $response->json('submission_id');
|
||||||
|
expect($submissionId)->toBeString();
|
||||||
|
$response = $this->getJson(route('forms.fetchSubmission', [$form->slug, $submissionId]))
|
||||||
|
->assertSuccessful();
|
||||||
|
$uuid = $response->json('data.uuid_field');
|
||||||
|
expect(Str::isUuid($uuid))->toBeTrue();
|
||||||
|
|
||||||
|
if ($submissionId) {
|
||||||
|
$formData = $this->generateFormSubmissionData($form, ['submission_id' => $submissionId, 'name' => 'Testing Updated', 'uuid_field' => $uuid]);
|
||||||
|
$response = $this->postJson(route('forms.answer', $form->slug), $formData)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertJson([
|
||||||
|
'type' => 'success',
|
||||||
|
'message' => 'Form submission saved.',
|
||||||
|
]);
|
||||||
|
$submissionId2 = $response->json('submission_id');
|
||||||
|
expect($submissionId2)->toBeString();
|
||||||
|
expect($submissionId2)->toBe($submissionId);
|
||||||
|
|
||||||
|
$response = $this->getJson(route('forms.fetchSubmission', [$form->slug, $submissionId]))
|
||||||
|
->assertSuccessful();
|
||||||
|
expect($response->json('data.name'))->toBe('Testing Updated');
|
||||||
|
$uuid2 = $response->json('data.uuid_field');
|
||||||
|
expect($uuid2)->toBe($uuid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,14 @@
|
||||||
v-if="isScanning"
|
v-if="isScanning"
|
||||||
class="relative w-full"
|
class="relative w-full"
|
||||||
>
|
>
|
||||||
|
<ClientOnly>
|
||||||
<CameraUpload
|
<CameraUpload
|
||||||
:is-barcode-mode="true"
|
:is-barcode-mode="true"
|
||||||
:decoders="decoders"
|
:decoders="decoders"
|
||||||
@stop-webcam="stopScanning"
|
@stop-webcam="stopScanning"
|
||||||
@barcode-detected="handleBarcodeDetected"
|
@barcode-detected="handleBarcodeDetected"
|
||||||
/>
|
/>
|
||||||
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,14 @@
|
||||||
theme.fileInput.minHeight
|
theme.fileInput.minHeight
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<camera-upload
|
<ClientOnly>
|
||||||
|
<CameraUpload
|
||||||
v-if="cameraUpload"
|
v-if="cameraUpload"
|
||||||
:theme="theme"
|
:theme="theme"
|
||||||
@upload-image="cameraFileUpload"
|
@upload-image="cameraFileUpload"
|
||||||
@stop-webcam="isInWebcam=false"
|
@stop-webcam="isInWebcam=false"
|
||||||
/>
|
/>
|
||||||
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<input-wrapper v-bind="inputWrapperProps">
|
<InputWrapper v-bind="inputWrapperProps">
|
||||||
<template #label>
|
<template #label>
|
||||||
<slot name="label" />
|
<slot name="label" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<span class="inline-block w-full rounded-md shadow-sm">
|
<span class="inline-block w-full rounded-md shadow-xs">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-haspopup="listbox"
|
aria-haspopup="listbox"
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
aria-labelledby="listbox-label"
|
aria-labelledby="listbox-label"
|
||||||
class="cursor-pointer relative w-full"
|
class="cursor-pointer relative w-full"
|
||||||
:class="[
|
:class="[
|
||||||
theme.default.input,
|
theme.default.input,
|
||||||
theme.default.spacing.horizontal,
|
theme.default.spacing.horizontal,
|
||||||
theme.default.spacing.vertical,
|
theme.default.spacing.vertical,
|
||||||
theme.default.fontSize,
|
theme.default.fontSize,
|
||||||
|
|
@ -24,52 +24,37 @@
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="currentUrl == null"
|
v-if="currentUrl == null"
|
||||||
class="text-gray-600 dark:text-gray-400"
|
class="text-gray-600 dark:text-gray-400 flex justify-center"
|
||||||
>
|
>
|
||||||
<svg
|
<Icon
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
name="heroicons:cloud-arrow-up"
|
||||||
class="h-5 w-5 inline"
|
class="h-5 w-5"
|
||||||
fill="none"
|
/>
|
||||||
viewBox="0 0 24 24"
|
<span class="ml-2">
|
||||||
stroke="currentColor"
|
Upload
|
||||||
>
|
</span>
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Upload image
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="h-6 text-gray-600 dark:text-gray-400 flex"
|
class=" text-gray-600 dark:text-gray-400 flex"
|
||||||
>
|
>
|
||||||
<div class="flex-grow">
|
<div class="flex-grow">
|
||||||
<img
|
<img
|
||||||
:src="currentUrl"
|
:src="tmpFile ?? currentUrl"
|
||||||
class="h-6 rounded shadow-md"
|
class="h-5 rounded shadow-md border"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="hover:text-nt-blue flex"
|
class="text-gray-500 hover:text-red-500 flex items-center"
|
||||||
@click.prevent="clearUrl"
|
@click.prevent="clearUrl"
|
||||||
>
|
>
|
||||||
<svg
|
<Icon
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
name="heroicons:trash"
|
||||||
class="h-6 w-6"
|
class="h-5 w-5"
|
||||||
fill="none"
|
/>
|
||||||
viewBox="0 0 24 24"
|
</a>
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
||||||
/></svg></a>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -105,7 +90,7 @@
|
||||||
v-if="loading"
|
v-if="loading"
|
||||||
class="text-gray-600 dark:text-gray-400"
|
class="text-gray-600 dark:text-gray-400"
|
||||||
>
|
>
|
||||||
<Loader class="h-6 w-6 mx-auto m-10" />
|
<loader class="h-5 w-5 mx-auto m-10" />
|
||||||
<p class="text-center mt-6">
|
<p class="text-center mt-6">
|
||||||
Uploading your file...
|
Uploading your file...
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -127,20 +112,10 @@
|
||||||
accept="image/png, image/gif, image/jpeg, image/bmp, image/svg+xml"
|
accept="image/png, image/gif, image/jpeg, image/bmp, image/svg+xml"
|
||||||
@change="manualFileUpload"
|
@change="manualFileUpload"
|
||||||
>
|
>
|
||||||
<svg
|
<Icon
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
name="heroicons:cloud-arrow-up"
|
||||||
class="mx-auto h-24 w-24 text-gray-200"
|
class="x-auto h-24 w-24 text-gray-200"
|
||||||
fill="none"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<p class="mt-5 text-sm text-gray-600">
|
<p class="mt-5 text-sm text-gray-600">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -161,7 +136,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</modal>
|
</modal>
|
||||||
</input-wrapper>
|
</InputWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
<template>
|
||||||
|
<input-wrapper v-bind="inputWrapperProps">
|
||||||
|
<template #label>
|
||||||
|
<slot name="label" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="grid"
|
||||||
|
:class="[gridClass, { 'gap-2': !seamless }]"
|
||||||
|
:style="optionStyle"
|
||||||
|
role="listbox"
|
||||||
|
:aria-multiselectable="multiple ? 'true' : 'false'"
|
||||||
|
:tabindex="disabled ? -1 : 0"
|
||||||
|
@keydown="onKeydown"
|
||||||
|
ref="root"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="(option, idx) in options"
|
||||||
|
:key="option[optionKey]"
|
||||||
|
class="flex flex-col items-center justify-center p-1.5 border transition-colors text-gray-500 focus:outline-none"
|
||||||
|
:class="[
|
||||||
|
option.class ? (typeof option.class === 'function' ? option.class(isSelected(option)) : option.class) : {},
|
||||||
|
{
|
||||||
|
'border-form-color text-form-color bg-form-color/10': isSelected(option),
|
||||||
|
'hover:bg-gray-100 border-gray-300': !isSelected(option),
|
||||||
|
'opacity-50 pointer-events-none': disabled || option.disabled,
|
||||||
|
// Seamless mode: only first and last have radius
|
||||||
|
'rounded-lg': !seamless,
|
||||||
|
'rounded-l-lg': seamless && idx === 0,
|
||||||
|
'rounded-r-lg': seamless && idx === options.length - 1,
|
||||||
|
// Seamless mode: overlap borders with negative margin, keep all borders
|
||||||
|
'-ml-px': seamless && idx > 0,
|
||||||
|
// Seamless mode: z-index hierarchy - selected > hovered/focused > default
|
||||||
|
'relative z-20': seamless && isSelected(option),
|
||||||
|
'relative z-10': seamless && !isSelected(option) && focusedIdx === idx,
|
||||||
|
'relative z-0': seamless && !isSelected(option) && focusedIdx !== idx,
|
||||||
|
// Add hover z-index for seamless mode (but lower than selected)
|
||||||
|
'hover:z-10': seamless && !isSelected(option)
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
:aria-selected="isSelected(option) ? 'true' : 'false'"
|
||||||
|
:tabindex="disabled || option.disabled ? -1 : 0"
|
||||||
|
:disabled="disabled || option.disabled"
|
||||||
|
@click="selectOption(option)"
|
||||||
|
@focus="focusedIdx = idx"
|
||||||
|
@mouseenter="focusedIdx = idx"
|
||||||
|
:title="option.tooltip || ''"
|
||||||
|
role="option"
|
||||||
|
>
|
||||||
|
<slot name="icon" :option="option" :selected="isSelected(option)">
|
||||||
|
<Icon
|
||||||
|
v-if="option.icon"
|
||||||
|
:name="isSelected(option) && option.selectedIcon ? option.selectedIcon : option.icon"
|
||||||
|
:class="[
|
||||||
|
'w-4 h-4',
|
||||||
|
option.label ? 'mb-1' : '',
|
||||||
|
isSelected(option) ? 'text-form-color' : 'text-inherit',
|
||||||
|
option.iconClass ? (typeof option.iconClass === 'function' ? option.iconClass(isSelected(option)) : option.iconClass) : {}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
<span
|
||||||
|
v-if="option.label || !option.icon"
|
||||||
|
class="text-xs"
|
||||||
|
:class="{
|
||||||
|
'text-form-color': isSelected(option),
|
||||||
|
'text-inherit': !isSelected(option),
|
||||||
|
}"
|
||||||
|
>{{ isSelected(option) ? option.selectedLabel ?? option.label : option.label }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #help>
|
||||||
|
<slot name="help" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #error>
|
||||||
|
<slot name="error" />
|
||||||
|
</template>
|
||||||
|
</input-wrapper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, nextTick } from 'vue'
|
||||||
|
import { inputProps, useFormInput } from './useFormInput.js'
|
||||||
|
import InputWrapper from './components/InputWrapper.vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OptionSelectorInput.vue
|
||||||
|
*
|
||||||
|
* A form input component for selecting options in a grid layout with icons.
|
||||||
|
* Integrates with the form system using InputWrapper and useFormInput.
|
||||||
|
*
|
||||||
|
* Props:
|
||||||
|
* - options: Array<{ name, label, icon, selectedIcon?, iconClass?, tooltip?, disabled? }>
|
||||||
|
* - multiple: Boolean (default: false)
|
||||||
|
* - optionKey: String (default: 'name')
|
||||||
|
* - columns: Number (default: 3, for grid layout)
|
||||||
|
* - seamless: Boolean (default: false, removes gaps and only applies radius to first/last items)
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Keyboard navigation (arrow keys, enter/space to select)
|
||||||
|
* - Focus management
|
||||||
|
* - Optional tooltips per option
|
||||||
|
* - Form validation integration
|
||||||
|
* - Notion-style look by default
|
||||||
|
* - Seamless mode for connected button appearance
|
||||||
|
*/
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
...inputProps,
|
||||||
|
options: { type: Array, required: true },
|
||||||
|
multiple: { type: Boolean, default: false },
|
||||||
|
optionKey: { type: String, default: 'name' },
|
||||||
|
columns: { type: Number, default: 3 },
|
||||||
|
seamless: { type: Boolean, default: false }
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
|
||||||
|
|
||||||
|
// Use form input composable
|
||||||
|
const {
|
||||||
|
compVal,
|
||||||
|
inputWrapperProps
|
||||||
|
} = useFormInput(props, { emit })
|
||||||
|
|
||||||
|
// Local state
|
||||||
|
const focusedIdx = ref(-1)
|
||||||
|
const root = ref(null)
|
||||||
|
|
||||||
|
// Computed properties
|
||||||
|
const gridClass = computed(() => `grid-cols-${props.columns}`)
|
||||||
|
|
||||||
|
const optionStyle = computed(() => ({
|
||||||
|
'--bg-form-color': props.color
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
function isSelected(option) {
|
||||||
|
if (props.multiple) {
|
||||||
|
return Array.isArray(compVal.value) && compVal.value.includes(option[props.optionKey])
|
||||||
|
}
|
||||||
|
return compVal.value === option[props.optionKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectOption(option) {
|
||||||
|
if (props.disabled || option.disabled) return
|
||||||
|
|
||||||
|
if (props.multiple) {
|
||||||
|
let newValue = Array.isArray(compVal.value) ? [...compVal.value] : []
|
||||||
|
const idx = newValue.indexOf(option[props.optionKey])
|
||||||
|
if (idx > -1) {
|
||||||
|
newValue.splice(idx, 1)
|
||||||
|
} else {
|
||||||
|
newValue.push(option[props.optionKey])
|
||||||
|
}
|
||||||
|
compVal.value = newValue
|
||||||
|
} else {
|
||||||
|
compVal.value = isSelected(option) ? null : option[props.optionKey]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeydown(e) {
|
||||||
|
if (props.disabled) return
|
||||||
|
const len = props.options.length
|
||||||
|
if (len === 0) return
|
||||||
|
|
||||||
|
if (["ArrowRight", "ArrowDown"].includes(e.key)) {
|
||||||
|
e.preventDefault()
|
||||||
|
focusedIdx.value = (focusedIdx.value + 1) % len
|
||||||
|
focusButton(focusedIdx.value)
|
||||||
|
} else if (["ArrowLeft", "ArrowUp"].includes(e.key)) {
|
||||||
|
e.preventDefault()
|
||||||
|
focusedIdx.value = (focusedIdx.value - 1 + len) % len
|
||||||
|
focusButton(focusedIdx.value)
|
||||||
|
} else if (["Enter", " ", "Spacebar"].includes(e.key)) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (focusedIdx.value >= 0 && focusedIdx.value < len) {
|
||||||
|
selectOption(props.options[focusedIdx.value])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusButton(idx) {
|
||||||
|
nextTick(() => {
|
||||||
|
const btns = root.value?.querySelectorAll('button')
|
||||||
|
if (btns && btns[idx]) btns[idx].focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watchers
|
||||||
|
watch(compVal, (val) => {
|
||||||
|
// Keep focus on selected
|
||||||
|
if (!props.multiple && val != null) {
|
||||||
|
const idx = props.options.findIndex(opt => opt[props.optionKey] === val)
|
||||||
|
if (idx !== -1) focusedIdx.value = idx
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
:class="[theme.fileInput.borderRadius]"
|
:class="[theme.fileInput.borderRadius]"
|
||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
id="webcam"
|
ref="webcamRef"
|
||||||
autoplay
|
autoplay
|
||||||
playsinline
|
playsinline
|
||||||
muted
|
muted
|
||||||
|
|
@ -12,17 +12,17 @@
|
||||||
{ hidden: !isCapturing },
|
{ hidden: !isCapturing },
|
||||||
theme.fileInput.minHeight,
|
theme.fileInput.minHeight,
|
||||||
theme.fileInput.borderRadius,
|
theme.fileInput.borderRadius,
|
||||||
'w-full h-full object-cover border border-gray-400/30'
|
'w-full h-full object-cover bg-gray-500'
|
||||||
]"
|
]"
|
||||||
webkit-playsinline
|
webkit-playsinline
|
||||||
/>
|
/>
|
||||||
<canvas
|
<canvas
|
||||||
id="canvas"
|
ref="canvasRef"
|
||||||
:class="[
|
:class="[
|
||||||
{ hidden: !capturedImage },
|
{ hidden: !capturedImage },
|
||||||
theme.fileInput.borderRadius,
|
theme.fileInput.borderRadius,
|
||||||
theme.fileInput.minHeight,
|
theme.fileInput.minHeight,
|
||||||
'w-full h-full object-cover border border-gray-400/30'
|
'w-full h-full object-cover'
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -31,23 +31,16 @@
|
||||||
v-if="isCapturing && isBarcodeMode"
|
v-if="isCapturing && isBarcodeMode"
|
||||||
class="absolute inset-0 pointer-events-none"
|
class="absolute inset-0 pointer-events-none"
|
||||||
>
|
>
|
||||||
<!-- Semi-transparent overlay -->
|
|
||||||
<div class="absolute inset-0 bg-black/30" />
|
|
||||||
|
|
||||||
<!-- Scanning area (transparent window) -->
|
<!-- Scanning area (transparent window) -->
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 flex items-center justify-center"
|
class="absolute inset-0 flex items-strech justify-center px-8 py-12"
|
||||||
style="padding-bottom: 60px;"
|
|
||||||
>
|
>
|
||||||
<div class="relative w-4/5 h-3/5">
|
<div class="flex-grow w-full relative">
|
||||||
<!-- Transparent window -->
|
|
||||||
<div class="absolute inset-0 bg-transparent border-0" />
|
|
||||||
|
|
||||||
<!-- Corner indicators -->
|
<!-- Corner indicators -->
|
||||||
<div class="absolute top-0 left-0 w-8 h-8 border-t-2 border-l-2 border-white" />
|
<div class="absolute top-0 left-0 w-8 h-8 border-t-2 border-l-2 rounded-tl-md border-white" />
|
||||||
<div class="absolute top-0 right-0 w-8 h-8 border-t-2 border-r-2 border-white" />
|
<div class="absolute top-0 right-0 w-8 h-8 border-t-2 border-r-2 rounded-tr-md border-white" />
|
||||||
<div class="absolute bottom-0 left-0 w-8 h-8 border-b-2 border-l-2 border-white" />
|
<div class="absolute bottom-0 left-0 w-8 h-8 border-b-2 border-l-2 rounded-bl-md border-white" />
|
||||||
<div class="absolute bottom-0 right-0 w-8 h-8 border-b-2 border-r-2 border-white" />
|
<div class="absolute bottom-0 right-0 w-8 h-8 border-b-2 border-r-2 rounded-br-md border-white" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -152,7 +145,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Webcam from "webcam-easy"
|
import Webcam from "webcam-easy"
|
||||||
import CachedDefaultTheme from "~/lib/forms/themes/CachedDefaultTheme.js"
|
import CachedDefaultTheme from "~/lib/forms/themes/CachedDefaultTheme.js"
|
||||||
import Quagga from 'quagga'
|
import { BrowserMultiFormatReader, DecodeHintType, BarcodeFormat } from '@zxing/library'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CameraUpload",
|
name: "CameraUpload",
|
||||||
|
|
@ -181,7 +174,7 @@ export default {
|
||||||
isCapturing: false,
|
isCapturing: false,
|
||||||
capturedImage: null,
|
capturedImage: null,
|
||||||
cameraPermissionStatus: "loading",
|
cameraPermissionStatus: "loading",
|
||||||
quaggaInitialized: false,
|
zxingReader: null,
|
||||||
currentFacingMode: 'user',
|
currentFacingMode: 'user',
|
||||||
mediaStream: null
|
mediaStream: null
|
||||||
}),
|
}),
|
||||||
|
|
@ -197,9 +190,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const webcamElement = document.getElementById("webcam")
|
// For regular camera mode, we still need the webcam.js setup
|
||||||
const canvasElement = document.getElementById("canvas")
|
if (!this.isBarcodeMode) {
|
||||||
this.webcam = new Webcam(webcamElement, "user", canvasElement)
|
this.webcam = new Webcam(this.$refs.webcamRef, "user", this.$refs.canvasRef)
|
||||||
|
}
|
||||||
this.openCameraUpload()
|
this.openCameraUpload()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -209,29 +203,27 @@ export default {
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async cleanupCurrentStream() {
|
async cleanupCurrentStream() {
|
||||||
if (this.quaggaInitialized) {
|
if (this.zxingReader) {
|
||||||
Quagga.stop()
|
this.zxingReader.reset()
|
||||||
this.quaggaInitialized = false
|
this.zxingReader = null
|
||||||
}
|
|
||||||
|
|
||||||
if (this.mediaStream) {
|
|
||||||
this.mediaStream.getTracks().forEach(track => track.stop())
|
|
||||||
this.mediaStream = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.webcam) {
|
if (this.webcam) {
|
||||||
this.webcam.stop()
|
this.webcam.stop()
|
||||||
|
this.webcam = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const webcamElement = document.getElementById("webcam")
|
// Clean up video element if needed
|
||||||
if (webcamElement && webcamElement.srcObject) {
|
if (this.$refs.webcamRef && this.$refs.webcamRef.srcObject) {
|
||||||
const tracks = webcamElement.srcObject.getTracks()
|
const tracks = this.$refs.webcamRef.srcObject.getTracks()
|
||||||
tracks.forEach(track => track.stop())
|
tracks.forEach(track => track.stop())
|
||||||
webcamElement.srcObject = null
|
this.$refs.webcamRef.srcObject = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async switchCamera() {
|
async switchCamera() {
|
||||||
|
if (!this.isMobileDevice) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Stop current camera and clean up resources
|
// Stop current camera and clean up resources
|
||||||
this.cleanupCurrentStream()
|
this.cleanupCurrentStream()
|
||||||
|
|
@ -240,7 +232,13 @@ export default {
|
||||||
this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user'
|
this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user'
|
||||||
|
|
||||||
// Restart camera
|
// Restart camera
|
||||||
await this.openCameraUpload()
|
if (this.isBarcodeMode) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.initZxingDirect()
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
await this.openCameraUpload()
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error switching camera:', error)
|
console.error('Error switching camera:', error)
|
||||||
this.cameraPermissionStatus = "unknown"
|
this.cameraPermissionStatus = "unknown"
|
||||||
|
|
@ -252,15 +250,18 @@ export default {
|
||||||
this.capturedImage = null
|
this.capturedImage = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const webcamElement = document.getElementById("webcam")
|
if (this.isBarcodeMode) {
|
||||||
const canvasElement = document.getElementById("canvas")
|
// For barcode mode, let ZXing handle everything
|
||||||
|
this.cameraPermissionStatus = "allowed"
|
||||||
|
setTimeout(() => {
|
||||||
|
this.initZxingDirect()
|
||||||
|
}, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular camera mode - use existing logic
|
||||||
// Determine the facing mode to use
|
// Determine the facing mode to use
|
||||||
let facingMode = this.currentFacingMode
|
let facingMode = this.currentFacingMode
|
||||||
if (this.isBarcodeMode && this.currentFacingMode === 'user') {
|
|
||||||
// Force environment mode for barcode scanning
|
|
||||||
facingMode = 'environment'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create constraints based on device capabilities
|
// Create constraints based on device capabilities
|
||||||
const constraints = {
|
const constraints = {
|
||||||
|
|
@ -293,25 +294,26 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mediaStream = stream // Store the stream reference
|
this.mediaStream = stream // Store the stream reference
|
||||||
webcamElement.srcObject = stream
|
this.$refs.webcamRef.srcObject = stream
|
||||||
|
|
||||||
this.webcam = new Webcam(
|
this.webcam = new Webcam(
|
||||||
webcamElement,
|
this.$refs.webcamRef,
|
||||||
facingMode,
|
facingMode,
|
||||||
canvasElement
|
this.$refs.canvasRef
|
||||||
)
|
)
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
webcamElement.onloadedmetadata = () => {
|
this.$refs.webcamRef.onloadedmetadata = () => {
|
||||||
webcamElement.play()
|
this.$refs.webcamRef.play().then(() => {
|
||||||
resolve()
|
resolve()
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Error playing video:', err)
|
||||||
|
resolve() // Continue anyway
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.cameraPermissionStatus = "allowed"
|
this.cameraPermissionStatus = "allowed"
|
||||||
if (this.isBarcodeMode) {
|
|
||||||
this.initQuagga()
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Camera error:', err)
|
console.error('Camera error:', err)
|
||||||
if (err.name === 'NotAllowedError' || err.toString().includes('Permission denied')) {
|
if (err.name === 'NotAllowedError' || err.toString().includes('Permission denied')) {
|
||||||
|
|
@ -321,55 +323,92 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initQuagga() {
|
initZxingDirect() {
|
||||||
if (!this.quaggaInitialized) {
|
if (this.zxingReader) {
|
||||||
Quagga.init({
|
this.zxingReader.reset()
|
||||||
inputStream: {
|
this.zxingReader = null
|
||||||
name: "Live",
|
|
||||||
type: "LiveStream",
|
|
||||||
target: document.getElementById("webcam"),
|
|
||||||
constraints: {
|
|
||||||
facingMode: this.currentFacingMode,
|
|
||||||
width: { min: 640 },
|
|
||||||
height: { min: 480 },
|
|
||||||
aspectRatio: { min: 1, max: 2 }
|
|
||||||
},
|
|
||||||
},
|
|
||||||
locator: {
|
|
||||||
patchSize: "medium",
|
|
||||||
halfSample: true
|
|
||||||
},
|
|
||||||
numOfWorkers: navigator.hardwareConcurrency || 4,
|
|
||||||
frequency: 10,
|
|
||||||
decoder: {
|
|
||||||
readers: this.decoders || []
|
|
||||||
},
|
|
||||||
locate: true
|
|
||||||
}, (err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('Quagga initialization failed:', err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.quaggaInitialized = true
|
|
||||||
Quagga.start()
|
|
||||||
|
|
||||||
Quagga.onDetected((result) => {
|
|
||||||
if (result.codeResult) {
|
|
||||||
this.$emit('barcodeDetected', result.codeResult.code)
|
|
||||||
this.cancelCamera()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hints = new Map()
|
||||||
|
const formats = (this.decoders || []).map(decoder => {
|
||||||
|
// Map decoder strings to BarcodeFormat enum values
|
||||||
|
// Remove _reader suffix for mapping
|
||||||
|
const cleanDecoder = decoder.replace('_reader', '').toLowerCase()
|
||||||
|
|
||||||
|
switch(cleanDecoder) {
|
||||||
|
case 'ean_8': return BarcodeFormat.EAN_8
|
||||||
|
case 'ean':
|
||||||
|
case 'ean_13': return BarcodeFormat.EAN_13
|
||||||
|
case 'upc':
|
||||||
|
case 'upc_a': return BarcodeFormat.UPC_A
|
||||||
|
case 'upc_e': return BarcodeFormat.UPC_E
|
||||||
|
case 'code_39': return BarcodeFormat.CODE_39
|
||||||
|
case 'code_93': return BarcodeFormat.CODE_93
|
||||||
|
case 'code_128': return BarcodeFormat.CODE_128
|
||||||
|
case 'codabar': return BarcodeFormat.CODABAR
|
||||||
|
case 'itf': return BarcodeFormat.ITF
|
||||||
|
case 'qr':
|
||||||
|
case 'qr_code': return BarcodeFormat.QR_CODE
|
||||||
|
case 'data_matrix': return BarcodeFormat.DATA_MATRIX
|
||||||
|
case 'aztec': return BarcodeFormat.AZTEC
|
||||||
|
case 'pdf_417': return BarcodeFormat.PDF_417
|
||||||
|
default:
|
||||||
|
console.warn('Unsupported barcode format:', decoder)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}).filter(format => format !== null)
|
||||||
|
|
||||||
|
if (formats.length > 0) {
|
||||||
|
hints.set(DecodeHintType.POSSIBLE_FORMATS, formats)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.zxingReader = new BrowserMultiFormatReader(hints)
|
||||||
|
|
||||||
|
// Use simple constraints approach instead of device enumeration
|
||||||
|
const facingMode = this.isMobileDevice && this.currentFacingMode === 'user' ? 'environment' : this.currentFacingMode
|
||||||
|
const constraints = {
|
||||||
|
audio: false,
|
||||||
|
video: {
|
||||||
|
facingMode: facingMode,
|
||||||
|
width: { ideal: 1280 },
|
||||||
|
height: { ideal: 720 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use ZXing's decodeFromConstraints method
|
||||||
|
this.zxingReader.decodeFromConstraints(constraints, this.$refs.webcamRef, (result, error) => {
|
||||||
|
if (result) {
|
||||||
|
this.$emit('barcodeDetected', result.text)
|
||||||
|
}
|
||||||
|
// Don't log NotFoundException errors - they're expected during scanning
|
||||||
|
// Only log other types of errors
|
||||||
|
else if (error && error.name && !error.name.includes('NotFoundException') && !error.message?.includes('No MultiFormat Readers')) {
|
||||||
|
console.error('ZXing decoding error:', error.name, error.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.cameraPermissionStatus = "allowed"
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Camera error in ZXing Direct:', err)
|
||||||
|
if (err.name === 'NotAllowedError' || err.toString().includes('Permission denied')) {
|
||||||
|
this.cameraPermissionStatus = "blocked"
|
||||||
|
} else {
|
||||||
|
this.cameraPermissionStatus = "unknown"
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
cancelCamera() {
|
cancelCamera() {
|
||||||
this.isCapturing = false
|
this.isCapturing = false
|
||||||
this.capturedImage = null
|
this.capturedImage = null
|
||||||
this.cleanupCurrentStream() // Use the cleanup method
|
this.cleanupCurrentStream() // Use the cleanup method
|
||||||
this.$emit("stopWebcam")
|
this.$emit("stopWebcam")
|
||||||
},
|
},
|
||||||
processCapturedImage() {
|
processCapturedImage() {
|
||||||
|
if (!this.webcam) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.capturedImage = this.webcam.snap()
|
this.capturedImage = this.webcam.snap()
|
||||||
this.isCapturing = false
|
this.isCapturing = false
|
||||||
this.webcam.stop()
|
this.webcam.stop()
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,8 @@ const isAutoSubmit = ref(import.meta.client && window.location.href.includes('au
|
||||||
|
|
||||||
// Create a reactive reference directly from the prop
|
// Create a reactive reference directly from the prop
|
||||||
const darkModeRef = toRef(props, 'darkMode')
|
const darkModeRef = toRef(props, 'darkMode')
|
||||||
|
// Create a reactive reference for the mode prop
|
||||||
|
const modeRef = toRef(props, 'mode')
|
||||||
|
|
||||||
// Add back the local theme computation
|
// Add back the local theme computation
|
||||||
const theme = computed(() => {
|
const theme = computed(() => {
|
||||||
|
|
@ -258,7 +260,8 @@ const theme = computed(() => {
|
||||||
let formManager = null
|
let formManager = null
|
||||||
if (props.form) {
|
if (props.form) {
|
||||||
formManager = useFormManager(props.form, props.mode, {
|
formManager = useFormManager(props.form, props.mode, {
|
||||||
darkMode: darkModeRef
|
darkMode: darkModeRef,
|
||||||
|
mode: modeRef
|
||||||
})
|
})
|
||||||
formManager.initialize({
|
formManager.initialize({
|
||||||
submissionId: submissionId.value,
|
submissionId: submissionId.value,
|
||||||
|
|
|
||||||
|
|
@ -17,70 +17,84 @@
|
||||||
:form="form"
|
:form="form"
|
||||||
label="Form Theme"
|
label="Form Theme"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<color-input
|
<color-input
|
||||||
name="color"
|
name="color"
|
||||||
:form="form"
|
:form="form"
|
||||||
|
label="Accent Color"
|
||||||
|
class="my-4"
|
||||||
>
|
>
|
||||||
<template #help>
|
<template #label>
|
||||||
<InputHelp>
|
<InputLabel>Accent Color - <a
|
||||||
<span class="text-gray-500">
|
href="#" class="text-blue-500"
|
||||||
Color (for buttons & inputs border) - <a
|
@click.prevent="form.color = DEFAULT_COLOR"
|
||||||
class="text-blue-500"
|
>Reset</a></InputLabel>
|
||||||
href="#"
|
|
||||||
@click.prevent="form.color = DEFAULT_COLOR"
|
|
||||||
>Reset</a>
|
|
||||||
</span>
|
|
||||||
</InputHelp>
|
|
||||||
</template>
|
</template>
|
||||||
</color-input>
|
</color-input>
|
||||||
<select-input
|
|
||||||
name="dark_mode"
|
<OptionSelectorInput
|
||||||
:options="[
|
v-model="form.dark_mode"
|
||||||
{ name: 'Auto', value: 'auto' },
|
|
||||||
{ name: 'Light Mode', value: 'light' },
|
|
||||||
{ name: 'Dark Mode', value: 'dark' },
|
|
||||||
]"
|
|
||||||
:form="form"
|
:form="form"
|
||||||
|
name="dark_mode"
|
||||||
label="Color Mode"
|
label="Color Mode"
|
||||||
help="Use Auto to use device system preferences"
|
:options="[
|
||||||
|
{ name: 'auto', label: 'System', icon: 'i-heroicons-computer-desktop' },
|
||||||
|
{ name: 'light', label: 'Light', icon: 'i-heroicons-sun' },
|
||||||
|
{ name: 'dark', label: 'Dark', icon: 'i-heroicons-moon' },
|
||||||
|
]"
|
||||||
|
:multiple="false"
|
||||||
|
:columns="3"
|
||||||
|
class="mb-4"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<EditorSectionHeader
|
<EditorSectionHeader
|
||||||
icon="octicon:typography-16"
|
icon="octicon:typography-16"
|
||||||
title="Typography"
|
title="Text & Language"
|
||||||
/>
|
/>
|
||||||
<template v-if="useFeatureFlag('services.google.fonts')">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<label class="text-gray-700 font-medium text-sm">Font Style</label>
|
<div class="flex-grow my-1" v-if="useFeatureFlag('services.google.fonts')">
|
||||||
<v-button
|
<label class="text-gray-700 font-semibold text-sm mb-1 block">Font Family</label>
|
||||||
color="white"
|
<v-button
|
||||||
class="w-full mb-4"
|
color="white"
|
||||||
size="small"
|
class="w-full py-1.5"
|
||||||
@click="showGoogleFontPicker = true"
|
size="small"
|
||||||
>
|
@click="showGoogleFontPicker = true"
|
||||||
<span :style="{ 'font-family': (form.font_family?form.font_family+' !important':null) }">
|
>
|
||||||
{{ form.font_family || 'Default' }}
|
<span :style="{ 'font-family': (form.font_family ? form.font_family + ' !important' : null) }">
|
||||||
</span>
|
{{ form.font_family || 'Default' }}
|
||||||
</v-button>
|
</span>
|
||||||
<GoogleFontPicker
|
</v-button>
|
||||||
:show="showGoogleFontPicker"
|
<GoogleFontPicker
|
||||||
:font="form.font_family || null"
|
:show="showGoogleFontPicker"
|
||||||
@close="showGoogleFontPicker=false"
|
:font="form.font_family || null"
|
||||||
@apply="onApplyFont"
|
@close="showGoogleFontPicker = false"
|
||||||
/>
|
@apply="onApplyFont"
|
||||||
</template>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-grow">
|
||||||
|
<select-input
|
||||||
|
name="language"
|
||||||
|
searchable
|
||||||
|
:options="availableLocales"
|
||||||
|
:form="form"
|
||||||
|
label="Language"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ToggleSwitchInput
|
||||||
|
name="layout_rtl"
|
||||||
|
:form="form"
|
||||||
|
class="mt-4"
|
||||||
|
label="Right-to-Left Layout"
|
||||||
|
/>
|
||||||
|
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="uppercase_labels"
|
name="uppercase_labels"
|
||||||
:form="form"
|
:form="form"
|
||||||
label="Uppercase Input Labels"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<select-input
|
|
||||||
name="language"
|
|
||||||
class="mt-4"
|
class="mt-4"
|
||||||
searchable
|
label="Uppercase Input Labels"
|
||||||
:options="availableLocales"
|
|
||||||
:form="form"
|
|
||||||
label="Form Language"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<EditorSectionHeader
|
<EditorSectionHeader
|
||||||
|
|
@ -88,74 +102,87 @@
|
||||||
title="Layout & Sizing"
|
title="Layout & Sizing"
|
||||||
/>
|
/>
|
||||||
<div class="flex space-x-4 justify-stretch">
|
<div class="flex space-x-4 justify-stretch">
|
||||||
<select-input
|
<div class="flex-grow">
|
||||||
name="size"
|
<OptionSelectorInput
|
||||||
class="flex-grow"
|
seamless
|
||||||
:options="[
|
label="Input Size"
|
||||||
{ name: 'Small', value: 'sm' },
|
v-model="form.size"
|
||||||
{ name: 'Medium', value: 'md' },
|
:form="form"
|
||||||
{ name: 'Large', value: 'lg' },
|
name="size"
|
||||||
]"
|
:options="[
|
||||||
:form="form"
|
{ name: 'sm', label:'S'},
|
||||||
label="Input Size"
|
{ name: 'md', label:'M' },
|
||||||
/>
|
{ name: 'lg', label:'L' },
|
||||||
|
]"
|
||||||
|
:multiple="false"
|
||||||
|
:columns="3"
|
||||||
|
class="mb-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<select-input
|
<div class="flex-grow">
|
||||||
name="border_radius"
|
<OptionSelectorInput
|
||||||
class="flex-grow"
|
label="Input Roundness"
|
||||||
:options="[
|
v-model="form.border_radius"
|
||||||
{ name: 'None', value: 'none' },
|
seamless
|
||||||
{ name: 'Small', value: 'small' },
|
:form="form"
|
||||||
{ name: 'Full', value: 'full' },
|
name="border_radius"
|
||||||
]"
|
:options="[
|
||||||
:form="form"
|
{ name: 'none', icon: 'i-tabler-border-corner-square' },
|
||||||
label="Input Roundness"
|
{ name: 'small', icon: 'i-tabler-border-corner-rounded' },
|
||||||
/>
|
{ name: 'full', icon: 'i-tabler-border-corner-pill' },
|
||||||
|
]"
|
||||||
|
:multiple="false"
|
||||||
|
:columns="3"
|
||||||
|
class="mb-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<select-input
|
|
||||||
name="width"
|
<OptionSelectorInput
|
||||||
:options="[
|
v-model="form.width"
|
||||||
{ name: 'Centered', value: 'centered' },
|
|
||||||
{ name: 'Full Width', value: 'full' },
|
|
||||||
]"
|
|
||||||
:form="form"
|
|
||||||
label="Form Width"
|
label="Form Width"
|
||||||
help="Useful when embedding your form"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleSwitchInput
|
|
||||||
name="layout_rtl"
|
|
||||||
:form="form"
|
:form="form"
|
||||||
class="mt-4"
|
name="width"
|
||||||
label="Right-to-Left Layout"
|
seamless
|
||||||
help="Adjusts layout for RTL languages"
|
:options="[
|
||||||
|
{ name: 'centered', label: 'Centered' },
|
||||||
|
{ name: 'full', label: 'Full Width' },
|
||||||
|
]"
|
||||||
|
:multiple="false"
|
||||||
|
:columns="2"
|
||||||
|
class="mb-4 w-2/3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<EditorSectionHeader
|
<EditorSectionHeader
|
||||||
icon="heroicons:tag-16-solid"
|
icon="heroicons:tag-16-solid"
|
||||||
title="Branding & Media"
|
title="Branding & Media"
|
||||||
/>
|
/>
|
||||||
<image-input
|
<div class="grid grid-cols-2 gap-4">
|
||||||
name="logo_picture"
|
<image-input
|
||||||
:form="form"
|
name="logo_picture"
|
||||||
label="Logo"
|
:form="form"
|
||||||
help="Not visible when form is embedded"
|
label="Logo"
|
||||||
:required="false"
|
:required="false"
|
||||||
/>
|
/>
|
||||||
<image-input
|
|
||||||
name="cover_picture"
|
<image-input
|
||||||
:form="form"
|
name="cover_picture"
|
||||||
label="Cover image"
|
:form="form"
|
||||||
help="Not visible when form is embedded"
|
label="Cover (~1500px)"
|
||||||
/>
|
:required="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="no_branding"
|
name="no_branding"
|
||||||
:form="form"
|
:form="form"
|
||||||
|
class="mt-4"
|
||||||
@update:model-value="onChangeNoBranding"
|
@update:model-value="onChangeNoBranding"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
Remove OpnForm Branding
|
Hide OpnForm Branding
|
||||||
</span>
|
</span>
|
||||||
<pro-tag
|
<pro-tag
|
||||||
upgrade-modal-title="Upgrade today to remove OpnForm branding"
|
upgrade-modal-title="Upgrade today to remove OpnForm branding"
|
||||||
|
|
@ -182,7 +209,7 @@
|
||||||
name="transparent_background"
|
name="transparent_background"
|
||||||
:form="form"
|
:form="form"
|
||||||
label="Transparent Background"
|
label="Transparent Background"
|
||||||
help="Only applies when form is embedded"
|
help="When form is embedded"
|
||||||
/>
|
/>
|
||||||
<toggle-switch-input
|
<toggle-switch-input
|
||||||
name="confetti_on_submission"
|
name="confetti_on_submission"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
class="border rounded-lg bg-white dark:bg-notion-dark w-full block shadow-sm transition-all flex flex-col"
|
class="border rounded-lg bg-white dark:bg-notion-dark w-full block shadow-sm transition-all flex flex-col"
|
||||||
:class="{ 'max-w-5xl': !isExpanded, 'h-full': isExpanded }"
|
:class="{ 'max-w-5xl': !isExpanded, 'h-full': isExpanded }"
|
||||||
>
|
>
|
||||||
<div class="w-full bg-white dark:bg-gray-950 border-b border-gray-300 dark:border-blue-900 dark:border-gray-700 rounded-t-lg p-1.5 px-4 flex items-center gap-x-1.5">
|
<div class="w-full bg-white dark:bg-gray-950 border-b border-gray-300 dark:border-blue-900 dark:border-gray-700 rounded-t-lg p-1.5 pl-4 pr-1.5 flex items-center gap-x-1.5">
|
||||||
<div class="bg-red-500 rounded-full w-2.5 h-2.5" />
|
<div class="bg-red-500 rounded-full w-2.5 h-2.5" />
|
||||||
<div class="bg-yellow-500 rounded-full w-2.5 h-2.5" />
|
<div class="bg-yellow-500 rounded-full w-2.5 h-2.5" />
|
||||||
<div class="bg-green-500 rounded-full w-2.5 h-2.5" />
|
<div class="bg-green-500 rounded-full w-2.5 h-2.5" />
|
||||||
|
|
|
||||||
|
|
@ -641,12 +641,13 @@ export default {
|
||||||
],
|
],
|
||||||
allCountries: countryCodes,
|
allCountries: countryCodes,
|
||||||
barcodeDecodersOptions: [
|
barcodeDecodersOptions: [
|
||||||
|
{ name: 'QR Code', value: 'qr_reader' },
|
||||||
{ name: 'EAN-13 (European Article Number)', value: 'ean_reader' },
|
{ name: 'EAN-13 (European Article Number)', value: 'ean_reader' },
|
||||||
{ name: 'EAN-8 (European Article Number)', value: 'ean_8_reader' },
|
{ name: 'EAN-8 (European Article Number)', value: 'ean_8_reader' },
|
||||||
{ name: 'UPC-A (Universal Product Code)', value: 'upc_reader' },
|
{ name: 'UPC-A (Universal Product Code)', value: 'upc_reader' },
|
||||||
{ name: 'UPC-E (Universal Product Code)', value: 'upc_e_reader' },
|
{ name: 'UPC-E (Universal Product Code)', value: 'upc_e_reader' },
|
||||||
{ name: 'Code 128', value: 'code_128_reader' },
|
{ name: 'Code 128', value: 'code_128_reader' },
|
||||||
{ name: 'Code 39', value: 'code_39_reader' },
|
{ name: 'Code 39', value: 'code_39_reader' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,61 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="grid grid-cols-3 gap-2">
|
<OptionSelectorInput
|
||||||
<button
|
:options="availableOptions"
|
||||||
v-for="option in availableOptions"
|
v-model="selectedOption"
|
||||||
:key="option.name"
|
:multiple="false"
|
||||||
class="flex flex-col items-center justify-center p-1.5 border rounded-lg transition-colors text-gray-500"
|
:disabled="false"
|
||||||
:class="[
|
:columns="3"
|
||||||
option.class ? (typeof option.class === 'function' ? option.class(isSelected(option.name)) : option.class) : {},
|
name="field_state"
|
||||||
{
|
/>
|
||||||
'border-blue-500 bg-blue-50': isSelected(option.name),
|
|
||||||
'hover:bg-gray-100 border-gray-300': !isSelected(option.name)
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
@click="toggleOption(option.name)"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
:name="isSelected(option.name) && option.selectedIcon ? option.selectedIcon : option.icon"
|
|
||||||
:class="[
|
|
||||||
'w-4 h-4 mb-1',
|
|
||||||
{
|
|
||||||
'text-blue-500': isSelected(option.name),
|
|
||||||
'text-inherit': !isSelected(option.name),
|
|
||||||
},
|
|
||||||
option.iconClass ? (typeof option.iconClass === 'function' ? option.iconClass(isSelected(option.name)) : option.iconClass) : {}
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="text-xs"
|
|
||||||
:class="{
|
|
||||||
'text-blue-500': isSelected(option.name),
|
|
||||||
'text-inherit': !isSelected(option.name),
|
|
||||||
}"
|
|
||||||
>{{ isSelected(option.name) ? option.selectedLabel ?? option.label : option.label }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
field: {
|
field: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
canBeDisabled: {
|
canBeDisabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
canBeRequired: {
|
canBeRequired: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
canBeHidden: {
|
canBeHidden: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const emit = defineEmits(['update:field'])
|
||||||
|
|
||||||
defineEmits(['update:field'])
|
const options = [
|
||||||
|
{
|
||||||
const options = ref([
|
name: 'required',
|
||||||
{
|
label: 'Required',
|
||||||
name: 'required',
|
icon: 'ph:asterisk-bold',
|
||||||
label: 'Required',
|
selectedIcon: 'ph:asterisk-bold',
|
||||||
icon: 'i-ph-asterisk-bold',
|
|
||||||
selectedIcon: 'i-ph-asterisk-bold',
|
|
||||||
iconClass: (isActive) => isActive ? 'text-red-500' : '',
|
iconClass: (isActive) => isActive ? 'text-red-500' : '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'hidden',
|
name: 'hidden',
|
||||||
label: 'Hidden',
|
label: 'Hidden',
|
||||||
icon: 'i-heroicons-eye',
|
icon: 'heroicons:eye',
|
||||||
selectedIcon: 'i-heroicons-eye-slash-solid',
|
selectedIcon: 'heroicons:eye-slash-solid',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'disabled',
|
name: 'disabled',
|
||||||
label: 'Disabled',
|
label: 'Disabled',
|
||||||
icon: 'i-heroicons-lock-open',
|
icon: 'heroicons:lock-open',
|
||||||
selectedIcon: 'i-heroicons-lock-closed-solid',
|
selectedIcon: 'heroicons:lock-closed-solid',
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
|
|
||||||
const availableOptions = computed(() => {
|
const availableOptions = computed(() => {
|
||||||
return options.value.filter(option => {
|
return options.filter(option => {
|
||||||
if (option.name === 'disabled') return props.canBeDisabled
|
if (option.name === 'disabled') return props.canBeDisabled
|
||||||
if (option.name === 'required') return props.canBeRequired
|
if (option.name === 'required') return props.canBeRequired
|
||||||
if (option.name === 'hidden') return props.canBeHidden
|
if (option.name === 'hidden') return props.canBeHidden
|
||||||
|
|
@ -89,28 +63,34 @@ const availableOptions = computed(() => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const isSelected = (optionName) => {
|
const selectedOption = computed({
|
||||||
return props.field[optionName]
|
get() {
|
||||||
}
|
// Only one can be true at a time, priority: required > hidden > disabled
|
||||||
|
if (props.field.required) return 'required'
|
||||||
const toggleOption = (optionName) => {
|
if (props.field.hidden) return 'hidden'
|
||||||
const newValue = !props.field[optionName]
|
if (props.field.disabled) return 'disabled'
|
||||||
|
return null
|
||||||
if (optionName === 'required' && newValue) {
|
},
|
||||||
props.field.hidden = false
|
set(optionName) {
|
||||||
} else if (optionName === 'hidden' && newValue) {
|
// Reset all
|
||||||
props.field.required = false
|
props.field.required = false
|
||||||
props.field.disabled = false
|
|
||||||
props.field.generates_uuid = false
|
|
||||||
props.field.generates_auto_increment_id = false
|
|
||||||
} else if (optionName === 'disabled' && newValue) {
|
|
||||||
props.field.hidden = false
|
props.field.hidden = false
|
||||||
|
props.field.disabled = false
|
||||||
|
// Apply business logic
|
||||||
|
if (optionName === 'required') {
|
||||||
|
props.field.required = true
|
||||||
|
props.field.hidden = false
|
||||||
|
} else if (optionName === 'hidden') {
|
||||||
|
props.field.hidden = true
|
||||||
|
props.field.required = false
|
||||||
|
props.field.disabled = false
|
||||||
|
props.field.generates_uuid = false
|
||||||
|
props.field.generates_auto_increment_id = false
|
||||||
|
} else if (optionName === 'disabled') {
|
||||||
|
props.field.disabled = true
|
||||||
|
props.field.hidden = false
|
||||||
|
}
|
||||||
|
emit('update:field', { ...props.field })
|
||||||
}
|
}
|
||||||
|
})
|
||||||
if ((optionName === 'disabled' && props.canBeDisabled) ||
|
|
||||||
(optionName === 'required' && props.canBeRequired) ||
|
|
||||||
(optionName === 'hidden' && props.canBeHidden)) {
|
|
||||||
props.field[optionName] = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
<div class="flex mt-2 items-center">
|
<div class="flex mt-2 items-center">
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-400 text-center w-full">
|
<p class="text-sm text-gray-600 dark:text-gray-400 text-center w-full">
|
||||||
© Copyright {{ currYear }}. All Rights Reserved
|
© Copyright {{ currYear }}. All Rights Reserved
|
||||||
|
<span v-if="version">
|
||||||
|
<br>Version {{ version }}
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center mt-5 md:mt-0">
|
<div class="flex justify-center mt-5 md:mt-0">
|
||||||
|
|
@ -82,22 +85,14 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { computed } from "vue"
|
|
||||||
import opnformConfig from "~/opnform.config.js"
|
import opnformConfig from "~/opnform.config.js"
|
||||||
|
|
||||||
export default {
|
const authStore = useAuthStore()
|
||||||
setup() {
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
return {
|
|
||||||
user: computed(() => authStore.user),
|
|
||||||
appStore: useAppStore(),
|
|
||||||
opnformConfig,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
const user = computed(() => authStore.user)
|
||||||
currYear: new Date().getFullYear(),
|
const currYear = ref(new Date().getFullYear())
|
||||||
}),
|
|
||||||
}
|
// Use the reactive version for proper template reactivity
|
||||||
|
const version = computed(() => useFeatureFlag('version'))
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -207,9 +207,22 @@
|
||||||
"text_class": "text-pink-900",
|
"text_class": "text-pink-900",
|
||||||
"is_input": true,
|
"is_input": true,
|
||||||
"default_values": {
|
"default_values": {
|
||||||
"decoders": ["ean_reader", "ean_8_reader"]
|
"decoders": ["qr_reader", "ean_reader", "ean_8_reader"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"qrcode": {
|
||||||
|
"name": "qrcode",
|
||||||
|
"title": "QR Code Reader",
|
||||||
|
"icon": "i-material-symbols-qr-code-scanner-rounded",
|
||||||
|
"default_block_name": "Scan QR Code",
|
||||||
|
"bg_class": "bg-pink-100",
|
||||||
|
"text_class": "text-pink-900",
|
||||||
|
"is_input": true,
|
||||||
|
"default_values": {
|
||||||
|
"decoders": ["qr_reader", "ean_reader", "ean_8_reader"]
|
||||||
|
},
|
||||||
|
"actual_input": "barcode"
|
||||||
|
},
|
||||||
"payment": {
|
"payment": {
|
||||||
"name": "payment",
|
"name": "payment",
|
||||||
"title": "Payment",
|
"title": "Payment",
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,17 @@ import { cloneDeep } from 'lodash'
|
||||||
* Initializes and coordinates various form composables (Structure, Init, Validation, etc.)
|
* Initializes and coordinates various form composables (Structure, Init, Validation, etc.)
|
||||||
* based on the provided form configuration and mode.
|
* based on the provided form configuration and mode.
|
||||||
*/
|
*/
|
||||||
export function useFormManager(initialFormConfig, mode = FormMode.LIVE, options = {}) {
|
export function useFormManager(initialFormConfig, initialMode = FormMode.LIVE, options = {}) {
|
||||||
// --- Reactive State ---
|
// --- Reactive State ---
|
||||||
const config = ref(initialFormConfig) // Use ref for potentially replaceable config
|
const config = ref(initialFormConfig) // Use ref for potentially replaceable config
|
||||||
const form = useForm() // Core vForm instance
|
const form = useForm() // Core vForm instance
|
||||||
const strategy = computed(() => createFormModeStrategy(mode)) // Strategy based on mode
|
|
||||||
|
// Make mode reactive - accept either a ref or a static value
|
||||||
|
const mode = options.mode && typeof options.mode === 'object' && 'value' in options.mode
|
||||||
|
? options.mode
|
||||||
|
: ref(initialMode)
|
||||||
|
|
||||||
|
const strategy = computed(() => createFormModeStrategy(mode.value)) // Strategy based on reactive mode
|
||||||
|
|
||||||
// Use the passed darkMode ref if it's a ref, otherwise create a new ref
|
// Use the passed darkMode ref if it's a ref, otherwise create a new ref
|
||||||
const darkMode = options.darkMode && typeof options.darkMode === 'object' && 'value' in options.darkMode
|
const darkMode = options.darkMode && typeof options.darkMode === 'object' && 'value' in options.darkMode
|
||||||
|
|
@ -117,7 +123,7 @@ export function useFormManager(initialFormConfig, mode = FormMode.LIVE, options
|
||||||
const paymentBlock = structure.currentPagePaymentBlock.value
|
const paymentBlock = structure.currentPagePaymentBlock.value
|
||||||
if (paymentBlock) {
|
if (paymentBlock) {
|
||||||
// In editor/test mode (not LIVE), skip payment validation
|
// In editor/test mode (not LIVE), skip payment validation
|
||||||
const isPaymentRequired = mode === FormMode.LIVE ? !!paymentBlock.required : false
|
const isPaymentRequired = mode.value === FormMode.LIVE ? !!paymentBlock.required : false
|
||||||
|
|
||||||
// Pass required refs if Stripe needs them now (unlikely for just intent creation)
|
// Pass required refs if Stripe needs them now (unlikely for just intent creation)
|
||||||
const paymentResult = await payment.processPayment(paymentBlock, isPaymentRequired)
|
const paymentResult = await payment.processPayment(paymentBlock, isPaymentRequired)
|
||||||
|
|
@ -179,7 +185,7 @@ export function useFormManager(initialFormConfig, mode = FormMode.LIVE, options
|
||||||
if (paymentBlock) {
|
if (paymentBlock) {
|
||||||
|
|
||||||
// In editor/test mode (not LIVE), skip payment validation
|
// In editor/test mode (not LIVE), skip payment validation
|
||||||
const isPaymentRequired = mode === FormMode.LIVE ? !!paymentBlock.required : false
|
const isPaymentRequired = mode.value === FormMode.LIVE ? !!paymentBlock.required : false
|
||||||
|
|
||||||
const paymentResult = await payment.processPayment(paymentBlock, isPaymentRequired)
|
const paymentResult = await payment.processPayment(paymentBlock, isPaymentRequired)
|
||||||
|
|
||||||
|
|
@ -316,6 +322,8 @@ export function useFormManager(initialFormConfig, mode = FormMode.LIVE, options
|
||||||
// UI-related properties
|
// UI-related properties
|
||||||
darkMode, // Dark mode setting
|
darkMode, // Dark mode setting
|
||||||
setDarkMode: (isDark) => { darkMode.value = isDark }, // Method to update dark mode
|
setDarkMode: (isDark) => { darkMode.value = isDark }, // Method to update dark mode
|
||||||
|
mode, // Form mode setting (ref)
|
||||||
|
setMode: (newMode) => { mode.value = newMode }, // Method to update form mode
|
||||||
|
|
||||||
// Composables (Expose if direct access needed, often not necessary)
|
// Composables (Expose if direct access needed, often not necessary)
|
||||||
structure,
|
structure,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
export default defineNuxtRouteMiddleware(async () => {
|
export default defineNuxtRouteMiddleware(async () => {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const featureFlagsStore = useFeatureFlagsStore()
|
|
||||||
|
|
||||||
// Ensure feature flags are loaded
|
|
||||||
if (!featureFlagsStore.isLoaded) {
|
|
||||||
await featureFlagsStore.fetchFlags()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useFeatureFlag('self_hosted')) {
|
if (useFeatureFlag('self_hosted')) {
|
||||||
if (authStore.check && authStore.user?.email === 'admin@opnform.com') {
|
if (authStore.check && authStore.user?.email === 'admin@opnform.com') {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export default defineNuxtConfig({
|
||||||
],
|
],
|
||||||
|
|
||||||
build: {
|
build: {
|
||||||
transpile: ["vue-notion", "query-builder-vue-3", "vue-signature-pad"],
|
transpile: ["vue-notion", "query-builder-vue-3", "vue-signature-pad", "@zxing/library"],
|
||||||
},
|
},
|
||||||
|
|
||||||
i18n: {
|
i18n: {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
"@vueuse/integrations": "^11.2.0",
|
"@vueuse/integrations": "^11.2.0",
|
||||||
"@vueuse/motion": "^2.2.6",
|
"@vueuse/motion": "^2.2.6",
|
||||||
"@vueuse/nuxt": "^11.2.0",
|
"@vueuse/nuxt": "^11.2.0",
|
||||||
|
"@zxing/library": "^0.21.3",
|
||||||
"amplitude-js": "^8.21.9",
|
"amplitude-js": "^8.21.9",
|
||||||
"chart.js": "^4.4.5",
|
"chart.js": "^4.4.5",
|
||||||
"clone-deep": "^4.0.1",
|
"clone-deep": "^4.0.1",
|
||||||
|
|
@ -36,7 +37,6 @@
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"quagga": "^0.12.1",
|
|
||||||
"query-builder-vue-3": "^1.0.1",
|
"query-builder-vue-3": "^1.0.1",
|
||||||
"quill": "^2.0.2",
|
"quill": "^2.0.2",
|
||||||
"tailwind-merge": "^2.5.4",
|
"tailwind-merge": "^2.5.4",
|
||||||
|
|
@ -57,6 +57,7 @@
|
||||||
"@iconify-json/clarity": "^1.2.1",
|
"@iconify-json/clarity": "^1.2.1",
|
||||||
"@iconify-json/ic": "^1.2.1",
|
"@iconify-json/ic": "^1.2.1",
|
||||||
"@iconify-json/octicon": "^1.2.1",
|
"@iconify-json/octicon": "^1.2.1",
|
||||||
|
"@iconify-json/tabler": "^1.2.1",
|
||||||
"@nuxt/eslint-config": "^1.3.0",
|
"@nuxt/eslint-config": "^1.3.0",
|
||||||
"@nuxt/icon": "^1.12.0",
|
"@nuxt/icon": "^1.12.0",
|
||||||
"@nuxtjs/i18n": "^9.0.0",
|
"@nuxtjs/i18n": "^9.0.0",
|
||||||
|
|
@ -1588,6 +1589,15 @@
|
||||||
"@iconify/types": "*"
|
"@iconify/types": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@iconify-json/tabler": {
|
||||||
|
"version": "1.2.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify-json/tabler/-/tabler-1.2.18.tgz",
|
||||||
|
"integrity": "sha512-W+8qiJhJpb4dmBw3P7JSM9QhGsFG8GIS3BJWAmrJ/92rzK6NPGUOPfGmswoO+/MuPzQV96ColY9lcUktUKv0pg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/types": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@iconify/collections": {
|
"node_modules/@iconify/collections": {
|
||||||
"version": "1.0.546",
|
"version": "1.0.546",
|
||||||
"resolved": "https://registry.npmjs.org/@iconify/collections/-/collections-1.0.546.tgz",
|
"resolved": "https://registry.npmjs.org/@iconify/collections/-/collections-1.0.546.tgz",
|
||||||
|
|
@ -9132,6 +9142,26 @@
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@zxing/library": {
|
||||||
|
"version": "0.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.21.3.tgz",
|
||||||
|
"integrity": "sha512-hZHqFe2JyH/ZxviJZosZjV+2s6EDSY0O24R+FQmlWZBZXP9IqMo7S3nb3+2LBWxodJQkSurdQGnqE7KXqrYgow==",
|
||||||
|
"dependencies": {
|
||||||
|
"ts-custom-error": "^3.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.4.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@zxing/text-encoding": "~0.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@zxing/text-encoding": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/abbrev": {
|
"node_modules/abbrev": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz",
|
||||||
|
|
@ -9214,6 +9244,7 @@
|
||||||
"version": "6.12.6",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
|
@ -9428,24 +9459,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/asn1": {
|
|
||||||
"version": "0.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": "~2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/assert-plus": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ast-kit": {
|
"node_modules/ast-kit": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-1.4.3.tgz",
|
||||||
|
|
@ -9496,12 +9509,6 @@
|
||||||
"integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==",
|
"integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/asynckit": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/at-least-node": {
|
"node_modules/at-least-node": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
||||||
|
|
@ -9548,21 +9555,6 @@
|
||||||
"postcss": "^8.1.0"
|
"postcss": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-sign2": {
|
|
||||||
"version": "0.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
|
||||||
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/aws4": {
|
|
||||||
"version": "1.13.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
|
|
||||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/b4a": {
|
"node_modules/b4a": {
|
||||||
"version": "1.6.7",
|
"version": "1.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
|
||||||
|
|
@ -9602,15 +9594,6 @@
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/bcrypt-pbkdf": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"tweetnacl": "^0.14.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
|
|
@ -10021,12 +10004,6 @@
|
||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
"node_modules/caseless": {
|
|
||||||
"version": "0.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
|
||||||
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||||
|
|
@ -10287,18 +10264,6 @@
|
||||||
"text-hex": "1.0.x"
|
"text-hex": "1.0.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/combined-stream": {
|
|
||||||
"version": "1.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"delayed-stream": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "8.3.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||||
|
|
@ -10780,33 +10745,6 @@
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cwise-compiler": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"uniq": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dashdash": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/data-uri-to-buffer": {
|
|
||||||
"version": "0.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz",
|
|
||||||
"integrity": "sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/date-fns": {
|
"node_modules/date-fns": {
|
||||||
"version": "2.30.0",
|
"version": "2.30.0",
|
||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
||||||
|
|
@ -10981,15 +10919,6 @@
|
||||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/delayed-stream": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/delegates": {
|
"node_modules/delegates": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
|
|
@ -11406,16 +11335,6 @@
|
||||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ecc-jsbn": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
|
||||||
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"jsbn": "~0.1.0",
|
|
||||||
"safer-buffer": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
|
@ -12313,12 +12232,6 @@
|
||||||
"integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==",
|
"integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/extend": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/externality": {
|
"node_modules/externality": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/externality/-/externality-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/externality/-/externality-1.0.2.tgz",
|
||||||
|
|
@ -12372,19 +12285,11 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/extsprintf": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
],
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-diff": {
|
"node_modules/fast-diff": {
|
||||||
|
|
@ -12431,6 +12336,7 @@
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-levenshtein": {
|
"node_modules/fast-levenshtein": {
|
||||||
|
|
@ -12642,50 +12548,6 @@
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/forever-agent": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/form-data": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.6",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/form-data/node_modules/mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/form-data/node_modules/mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/formdata-polyfill": {
|
"node_modules/formdata-polyfill": {
|
||||||
"version": "4.0.10",
|
"version": "4.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
|
|
@ -12865,55 +12727,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-pixels": {
|
|
||||||
"version": "3.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-pixels/-/get-pixels-3.3.3.tgz",
|
|
||||||
"integrity": "sha512-5kyGBn90i9tSMUVHTqkgCHsoWoR+/lGbl4yC83Gefyr0HLIhgSWEx/2F/3YgsZ7UpYNuM6pDhDK7zebrUJ5nXg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"data-uri-to-buffer": "0.0.3",
|
|
||||||
"jpeg-js": "^0.4.1",
|
|
||||||
"mime-types": "^2.0.1",
|
|
||||||
"ndarray": "^1.0.13",
|
|
||||||
"ndarray-pack": "^1.1.1",
|
|
||||||
"node-bitmap": "0.0.1",
|
|
||||||
"omggif": "^1.0.5",
|
|
||||||
"parse-data-uri": "^0.2.0",
|
|
||||||
"pngjs": "^3.3.3",
|
|
||||||
"request": "^2.44.0",
|
|
||||||
"through": "^2.3.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-pixels/node_modules/mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-pixels/node_modules/mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-pixels/node_modules/pngjs": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
|
||||||
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-port-please": {
|
"node_modules/get-port-please": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz",
|
||||||
|
|
@ -12958,15 +12771,6 @@
|
||||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/getpass": {
|
|
||||||
"version": "0.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
|
||||||
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/giget": {
|
"node_modules/giget": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
|
||||||
|
|
@ -13003,24 +12807,6 @@
|
||||||
"git-up": "^8.1.0"
|
"git-up": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/gl-mat2": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/gl-mat2/-/gl-mat2-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-oHgZ3DalAo9qAhMZM9QigXosqotcUCsgxarwrinipaqfSHvacI79Dzs72gY+oT4Td1kDQKEsG0RyX6mb02VVHA==",
|
|
||||||
"license": "zlib"
|
|
||||||
},
|
|
||||||
"node_modules/gl-vec2": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/gl-vec2/-/gl-vec2-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-YiqaAuNsheWmUV0Sa8k94kBB0D6RWjwZztyO+trEYS8KzJ6OQB/4686gdrf59wld4hHFIvaxynO3nRxpk1Ij/A==",
|
|
||||||
"license": "zlib"
|
|
||||||
},
|
|
||||||
"node_modules/gl-vec3": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/gl-vec3/-/gl-vec3-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw==",
|
|
||||||
"license": "zlib"
|
|
||||||
},
|
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "9.3.5",
|
"version": "9.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz",
|
||||||
|
|
@ -13199,29 +12985,6 @@
|
||||||
"h3": "^1.6.0"
|
"h3": "^1.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/har-schema": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/har-validator": {
|
|
||||||
"version": "5.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
|
||||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
|
||||||
"deprecated": "this library is no longer supported",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ajv": "^6.12.3",
|
|
||||||
"har-schema": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
|
@ -13373,21 +13136,6 @@
|
||||||
"node": ">= 0.12.0"
|
"node": ">= 0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-signature": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"jsprim": "^1.2.2",
|
|
||||||
"sshpk": "^1.7.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8",
|
|
||||||
"npm": ">=1.3.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/https-proxy-agent": {
|
"node_modules/https-proxy-agent": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
|
|
@ -13597,12 +13345,6 @@
|
||||||
"url": "https://opencollective.com/ioredis"
|
"url": "https://opencollective.com/ioredis"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/iota-array": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
|
@ -13640,12 +13382,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-buffer": {
|
|
||||||
"version": "1.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
|
||||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/is-builtin-module": {
|
"node_modules/is-builtin-module": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz",
|
||||||
|
|
@ -13877,12 +13613,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-typedarray": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/is-url": {
|
"node_modules/is-url": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
|
||||||
|
|
@ -13964,12 +13694,6 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/isstream": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
|
||||||
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/jackspeak": {
|
"node_modules/jackspeak": {
|
||||||
"version": "3.4.3",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||||
|
|
@ -13994,12 +13718,6 @@
|
||||||
"jiti": "lib/jiti-cli.mjs"
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jpeg-js": {
|
|
||||||
"version": "0.4.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
|
|
||||||
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/js-sha256": {
|
"node_modules/js-sha256": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz",
|
||||||
|
|
@ -14025,12 +13743,6 @@
|
||||||
"js-yaml": "bin/js-yaml.js"
|
"js-yaml": "bin/js-yaml.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsbn": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
|
||||||
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/jsdoc-type-pratt-parser": {
|
"node_modules/jsdoc-type-pratt-parser": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz",
|
||||||
|
|
@ -14060,16 +13772,11 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/json-schema": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
|
||||||
"license": "(AFL-2.1 OR BSD-3-Clause)"
|
|
||||||
},
|
|
||||||
"node_modules/json-schema-traverse": {
|
"node_modules/json-schema-traverse": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/json-stable-stringify-without-jsonify": {
|
"node_modules/json-stable-stringify-without-jsonify": {
|
||||||
|
|
@ -14079,12 +13786,6 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/json-stringify-safe": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
|
|
@ -14146,21 +13847,6 @@
|
||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsprim": {
|
|
||||||
"version": "1.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
|
|
||||||
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "1.0.0",
|
|
||||||
"extsprintf": "1.3.0",
|
|
||||||
"json-schema": "0.4.0",
|
|
||||||
"verror": "1.10.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/junk": {
|
"node_modules/junk": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz",
|
||||||
|
|
@ -15246,32 +14932,6 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ndarray": {
|
|
||||||
"version": "1.0.19",
|
|
||||||
"resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz",
|
|
||||||
"integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"iota-array": "^1.0.0",
|
|
||||||
"is-buffer": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ndarray-linear-interpolate": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ndarray-linear-interpolate/-/ndarray-linear-interpolate-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-UN0f4+6XWsQzJ2pP5gVp+kKn5tJed6mA3K/L50uO619+7LKrjcSNdcerhpqxYaSkbxNJuEN76N05yBBJySnZDw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/ndarray-pack": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cwise-compiler": "^1.1.2",
|
|
||||||
"ndarray": "^1.0.13"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||||
|
|
@ -15883,14 +15543,6 @@
|
||||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/node-bitmap": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz",
|
|
||||||
"integrity": "sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=v0.6.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-domexception": {
|
"node_modules/node-domexception": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
|
|
@ -16865,15 +16517,6 @@
|
||||||
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/oauth-sign": {
|
|
||||||
"version": "0.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
|
||||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
|
@ -16927,12 +16570,6 @@
|
||||||
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/omggif": {
|
|
||||||
"version": "1.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
|
|
||||||
"integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/on-change": {
|
"node_modules/on-change": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-change/-/on-change-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-change/-/on-change-5.0.1.tgz",
|
||||||
|
|
@ -17231,15 +16868,6 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse-data-uri": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"data-uri-to-buffer": "0.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parse-gitignore": {
|
"node_modules/parse-gitignore": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse-gitignore/-/parse-gitignore-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-gitignore/-/parse-gitignore-2.0.0.tgz",
|
||||||
|
|
@ -17418,12 +17046,6 @@
|
||||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/performance-now": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/pg-int8": {
|
"node_modules/pg-int8": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
|
|
@ -18531,18 +18153,6 @@
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/psl": {
|
|
||||||
"version": "1.15.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
|
||||||
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"punycode": "^2.3.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/lupomontero"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
|
||||||
|
|
@ -18557,6 +18167,7 @@
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
|
|
@ -18733,24 +18344,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/quagga": {
|
|
||||||
"version": "0.12.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/quagga/-/quagga-0.12.1.tgz",
|
|
||||||
"integrity": "sha512-bb2N6eT7ss6Bg27sxQgv/CT96KQBkXa+4YeS1W8bhsaXxoWp8zOQbrOwFWEPxPDTNaWEl7hTs3ZB7OC4k3EY3Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"get-pixels": "^3.2.3",
|
|
||||||
"gl-mat2": "^1.0.0",
|
|
||||||
"gl-vec2": "^1.0.0",
|
|
||||||
"gl-vec3": "^1.0.3",
|
|
||||||
"lodash": "^4.17.4",
|
|
||||||
"ndarray": "^1.0.18",
|
|
||||||
"ndarray-linear-interpolate": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/quansync": {
|
"node_modules/quansync": {
|
||||||
"version": "0.2.10",
|
"version": "0.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz",
|
||||||
|
|
@ -19188,78 +18781,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/request": {
|
|
||||||
"version": "2.88.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
|
||||||
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
|
||||||
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"aws-sign2": "~0.7.0",
|
|
||||||
"aws4": "^1.8.0",
|
|
||||||
"caseless": "~0.12.0",
|
|
||||||
"combined-stream": "~1.0.6",
|
|
||||||
"extend": "~3.0.2",
|
|
||||||
"forever-agent": "~0.6.1",
|
|
||||||
"form-data": "~2.3.2",
|
|
||||||
"har-validator": "~5.1.3",
|
|
||||||
"http-signature": "~1.2.0",
|
|
||||||
"is-typedarray": "~1.0.0",
|
|
||||||
"isstream": "~0.1.2",
|
|
||||||
"json-stringify-safe": "~5.0.1",
|
|
||||||
"mime-types": "~2.1.19",
|
|
||||||
"oauth-sign": "~0.9.0",
|
|
||||||
"performance-now": "^2.1.0",
|
|
||||||
"qs": "~6.5.2",
|
|
||||||
"safe-buffer": "^5.1.2",
|
|
||||||
"tough-cookie": "~2.5.0",
|
|
||||||
"tunnel-agent": "^0.6.0",
|
|
||||||
"uuid": "^3.3.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/request/node_modules/mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/request/node_modules/mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/request/node_modules/qs": {
|
|
||||||
"version": "6.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
|
||||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/request/node_modules/uuid": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
|
||||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
|
||||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"uuid": "bin/uuid"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
|
@ -19589,6 +19110,7 @@
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
|
|
@ -20029,31 +19551,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sshpk": {
|
|
||||||
"version": "1.18.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
|
|
||||||
"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"asn1": "~0.2.3",
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"bcrypt-pbkdf": "^1.0.0",
|
|
||||||
"dashdash": "^1.12.0",
|
|
||||||
"ecc-jsbn": "~0.1.1",
|
|
||||||
"getpass": "^0.1.1",
|
|
||||||
"jsbn": "~0.1.0",
|
|
||||||
"safer-buffer": "^2.0.2",
|
|
||||||
"tweetnacl": "~0.14.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"sshpk-conv": "bin/sshpk-conv",
|
|
||||||
"sshpk-sign": "bin/sshpk-sign",
|
|
||||||
"sshpk-verify": "bin/sshpk-verify"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/stable-hash": {
|
"node_modules/stable-hash": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
|
||||||
|
|
@ -20818,12 +20315,6 @@
|
||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/through": {
|
|
||||||
"version": "2.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
|
||||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/tiny-invariant": {
|
"node_modules/tiny-invariant": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
|
|
@ -20924,19 +20415,6 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tough-cookie": {
|
|
||||||
"version": "2.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
|
||||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"psl": "^1.1.28",
|
|
||||||
"punycode": "^2.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tr46": {
|
"node_modules/tr46": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
|
@ -20965,6 +20443,14 @@
|
||||||
"typescript": ">=4.8.4"
|
"typescript": ">=4.8.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ts-custom-error": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ts-interface-checker": {
|
"node_modules/ts-interface-checker": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||||
|
|
@ -21007,24 +20493,6 @@
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/tunnel-agent": {
|
|
||||||
"version": "0.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
|
||||||
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tweetnacl": {
|
|
||||||
"version": "0.14.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
|
||||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
|
||||||
"license": "Unlicense"
|
|
||||||
},
|
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
|
|
@ -21200,12 +20668,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/uniq": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
|
@ -21573,6 +21035,7 @@
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
|
|
@ -21650,20 +21113,6 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/verror": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"extsprintf": "^1.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.3.5",
|
"version": "6.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"@iconify-json/clarity": "^1.2.1",
|
"@iconify-json/clarity": "^1.2.1",
|
||||||
"@iconify-json/ic": "^1.2.1",
|
"@iconify-json/ic": "^1.2.1",
|
||||||
"@iconify-json/octicon": "^1.2.1",
|
"@iconify-json/octicon": "^1.2.1",
|
||||||
|
"@iconify-json/tabler": "^1.2.1",
|
||||||
"@nuxt/eslint-config": "^1.3.0",
|
"@nuxt/eslint-config": "^1.3.0",
|
||||||
"@nuxt/icon": "^1.12.0",
|
"@nuxt/icon": "^1.12.0",
|
||||||
"@nuxtjs/i18n": "^9.0.0",
|
"@nuxtjs/i18n": "^9.0.0",
|
||||||
|
|
@ -49,6 +50,7 @@
|
||||||
"@vueuse/integrations": "^11.2.0",
|
"@vueuse/integrations": "^11.2.0",
|
||||||
"@vueuse/motion": "^2.2.6",
|
"@vueuse/motion": "^2.2.6",
|
||||||
"@vueuse/nuxt": "^11.2.0",
|
"@vueuse/nuxt": "^11.2.0",
|
||||||
|
"@zxing/library": "^0.21.3",
|
||||||
"amplitude-js": "^8.21.9",
|
"amplitude-js": "^8.21.9",
|
||||||
"chart.js": "^4.4.5",
|
"chart.js": "^4.4.5",
|
||||||
"clone-deep": "^4.0.1",
|
"clone-deep": "^4.0.1",
|
||||||
|
|
@ -64,7 +66,6 @@
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"quagga": "^0.12.1",
|
|
||||||
"query-builder-vue-3": "^1.0.1",
|
"query-builder-vue-3": "^1.0.1",
|
||||||
"quill": "^2.0.2",
|
"quill": "^2.0.2",
|
||||||
"tailwind-merge": "^2.5.4",
|
"tailwind-merge": "^2.5.4",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import { useFeatureFlagsStore } from '~/stores/featureFlags'
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(async () => {
|
|
||||||
const featureFlagsStore = useFeatureFlagsStore()
|
|
||||||
|
|
||||||
// Load flags if they haven't been loaded yet
|
|
||||||
if (!featureFlagsStore.isLoaded) {
|
|
||||||
await featureFlagsStore.fetchFlags()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
import { useFeatureFlagsStore } from '~/stores/featureFlags'
|
import { useFeatureFlagsStore } from '~/stores/featureFlags'
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin(async (nuxtApp) => {
|
||||||
const featureFlagsStore = useFeatureFlagsStore()
|
// Get the pinia instance for SSR compatibility
|
||||||
|
const { $pinia } = nuxtApp
|
||||||
|
|
||||||
nuxtApp.provide('featureFlag', (key, defaultValue = false) => {
|
try {
|
||||||
return featureFlagsStore.getFlag(key, defaultValue)
|
// Pass pinia instance for SSR compatibility
|
||||||
})
|
const featureFlagsStore = useFeatureFlagsStore($pinia)
|
||||||
|
|
||||||
|
// Fetch flags during SSR to prevent hydration mismatches
|
||||||
|
if (!featureFlagsStore.isLoaded) {
|
||||||
|
await featureFlagsStore.fetchFlags()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Feature flags plugin failed:', error)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -25,7 +25,8 @@ function mergeOptions(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
debounceWait: 300
|
debounceWait: 300,
|
||||||
|
ignoreKeys: [] // Keys to ignore in history tracking
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -34,6 +35,25 @@ function mergeOptions(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters out ignored keys from the state object
|
||||||
|
* @param {Object} state - The state object to filter
|
||||||
|
* @param {Array} ignoreKeys - Array of keys to ignore
|
||||||
|
* @returns {Object} Filtered state object
|
||||||
|
*/
|
||||||
|
function filterState(state, ignoreKeys) {
|
||||||
|
if (!ignoreKeys || ignoreKeys.length === 0) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredState = { ...state }
|
||||||
|
ignoreKeys.forEach(key => {
|
||||||
|
delete filteredState[key]
|
||||||
|
})
|
||||||
|
|
||||||
|
return filteredState
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds undo/redo functionality to a Pinia store.
|
* Adds undo/redo functionality to a Pinia store.
|
||||||
* @param {PiniaPluginContext} context - The context provided by Pinia.
|
* @param {PiniaPluginContext} context - The context provided by Pinia.
|
||||||
|
|
@ -46,7 +66,7 @@ const PiniaHistory = (context) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const mergedOptions = mergeOptions(history)
|
const mergedOptions = mergeOptions(history)
|
||||||
const {max, persistent, persistentStrategy} = mergedOptions
|
const {max, persistent, persistentStrategy, ignoreKeys} = mergedOptions
|
||||||
|
|
||||||
const $history = reactive({
|
const $history = reactive({
|
||||||
max,
|
max,
|
||||||
|
|
@ -54,19 +74,23 @@ const PiniaHistory = (context) => {
|
||||||
persistentStrategy,
|
persistentStrategy,
|
||||||
done: [],
|
done: [],
|
||||||
undone: [],
|
undone: [],
|
||||||
current: JSON.stringify(store.$state),
|
current: JSON.stringify(filterState(store.$state, ignoreKeys)),
|
||||||
trigger: true,
|
trigger: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const debouncedStoreUpdate = debounce((state) => {
|
const debouncedStoreUpdate = debounce((state) => {
|
||||||
if (hash($history.current) === hash(JSON.stringify(state))) { // Not a real change here
|
const filteredState = filterState(state, ignoreKeys)
|
||||||
|
const currentStateHash = hash($history.current)
|
||||||
|
const newStateHash = hash(JSON.stringify(filteredState))
|
||||||
|
|
||||||
|
if (currentStateHash === newStateHash) { // Not a real change here
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ($history.done.length >= max) $history.done.shift() // Remove oldest state if needed
|
if ($history.done.length >= max) $history.done.shift() // Remove oldest state if needed
|
||||||
|
|
||||||
$history.done.push($history.current)
|
$history.done.push($history.current)
|
||||||
$history.undone = [] // Clear redo history on new action
|
$history.undone = [] // Clear redo history on new action
|
||||||
$history.current = JSON.stringify(state)
|
$history.current = JSON.stringify(filteredState)
|
||||||
|
|
||||||
if (persistent) {
|
if (persistent) {
|
||||||
persistentStrategy.set(store, 'undo', $history.done)
|
persistentStrategy.set(store, 'undo', $history.done)
|
||||||
|
|
@ -90,7 +114,9 @@ const PiniaHistory = (context) => {
|
||||||
|
|
||||||
$history.undone.push($history.current) // Save current state for redo
|
$history.undone.push($history.current) // Save current state for redo
|
||||||
$history.trigger = false
|
$history.trigger = false
|
||||||
store.$patch(JSON.parse(state))
|
// Only patch the state that was tracked (filtered state)
|
||||||
|
const stateToRestore = JSON.parse(state)
|
||||||
|
store.$patch(stateToRestore)
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
$history.current = state
|
$history.current = state
|
||||||
$history.trigger = true
|
$history.trigger = true
|
||||||
|
|
@ -114,7 +140,9 @@ const PiniaHistory = (context) => {
|
||||||
|
|
||||||
$history.done.push($history.current) // Save current state for undo
|
$history.done.push($history.current) // Save current state for undo
|
||||||
$history.trigger = false
|
$history.trigger = false
|
||||||
store.$patch(JSON.parse(state))
|
// Only patch the state that was tracked (filtered state)
|
||||||
|
const stateToRestore = JSON.parse(state)
|
||||||
|
store.$patch(stateToRestore)
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
$history.current = state
|
$history.current = state
|
||||||
$history.trigger = true
|
$history.trigger = true
|
||||||
|
|
|
||||||
|
|
@ -175,35 +175,42 @@ export const useWorkingFormStore = defineStore("working_form", {
|
||||||
}
|
}
|
||||||
if (!this.content) return
|
if (!this.content) return
|
||||||
|
|
||||||
const block = blocksTypes[type]
|
const originalBlockDefinition = blocksTypes[type]
|
||||||
if (block?.self_hosted !== undefined && !block.self_hosted && useFeatureFlag('self_hosted')) {
|
const effectiveType = originalBlockDefinition?.actual_input || type
|
||||||
|
const effectiveBlockDefinition = blocksTypes[effectiveType]
|
||||||
|
|
||||||
|
if (originalBlockDefinition?.self_hosted !== undefined && !originalBlockDefinition.self_hosted && useFeatureFlag('self_hosted')) {
|
||||||
useAlert().error(block?.title + ' is not allowed on self hosted. Please use our hosted version.')
|
useAlert().error(block?.title + ' is not allowed on self hosted. Please use our hosted version.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (originalBlockDefinition?.auth_required && !useAuthStore().check) {
|
||||||
if (block?.auth_required && !useAuthStore().check) {
|
|
||||||
useAlert().error('Please login first to add this block')
|
useAlert().error('Please login first to add this block')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block?.max_count !== undefined) {
|
if (originalBlockDefinition?.max_count !== undefined) {
|
||||||
const currentCount = this.content.properties.filter(prop => prop && prop.type === type).length
|
const currentCount = this.content.properties.filter(prop => prop && prop.type === type).length
|
||||||
if (currentCount >= block.max_count) {
|
if (currentCount >= originalBlockDefinition.max_count) {
|
||||||
useAlert().error(`Only ${block.max_count} '${block.title}' block(s) allowed per form.`)
|
useAlert().error(`Only ${originalBlockDefinition.max_count} '${originalBlockDefinition.title}' block(s) allowed per form.`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
openSettings = true
|
openSettings = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.blockForm.type = type
|
this.blockForm.type = effectiveType
|
||||||
this.blockForm.name = blocksTypes[type]?.default_block_name || 'New Block'
|
this.blockForm.name = effectiveBlockDefinition?.default_block_name || 'New Block'
|
||||||
const newBlock = this.prefillDefault({ ...this.blockForm.data() })
|
const newBlock = this.prefillDefault({ ...this.blockForm.data() })
|
||||||
newBlock.id = generateUUID()
|
newBlock.id = generateUUID()
|
||||||
newBlock.hidden = false
|
newBlock.hidden = false
|
||||||
newBlock.help_position = "below_input"
|
newBlock.help_position = "below_input"
|
||||||
|
|
||||||
if (blocksTypes[type]?.default_values) {
|
// If the type was changed due to actual_input, apply original type's change settings
|
||||||
Object.assign(newBlock, blocksTypes[type].default_values)
|
if (originalBlockDefinition?.actual_input && originalBlockDefinition?.type_change_settings) {
|
||||||
|
Object.assign(newBlock, originalBlockDefinition.type_change_settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effectiveBlockDefinition?.default_values) {
|
||||||
|
Object.assign(newBlock, effectiveBlockDefinition.default_values)
|
||||||
}
|
}
|
||||||
|
|
||||||
const insertIndex = this.determineInsertIndex(index)
|
const insertIndex = this.determineInsertIndex(index)
|
||||||
|
|
@ -253,5 +260,7 @@ export const useWorkingFormStore = defineStore("working_form", {
|
||||||
this.setProperties(newFields)
|
this.setProperties(newFields)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
history: {}
|
history: {
|
||||||
|
ignoreKeys: ['structureService', 'blockForm']
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ module.exports = {
|
||||||
border: 'rgba(15, 15, 15, 0.1)',
|
border: 'rgba(15, 15, 15, 0.1)',
|
||||||
borderDark: 'rgba(255, 255, 255, 0.1)'
|
borderDark: 'rgba(255, 255, 255, 0.1)'
|
||||||
},
|
},
|
||||||
"form-color": "var(--bg-form-color)",
|
'form-color': 'rgb(from var(--form-color, var(--bg-form-color)) r g b / <alpha-value>)'
|
||||||
},
|
},
|
||||||
transitionProperty: {
|
transitionProperty: {
|
||||||
height: "height",
|
height: "height",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
---
|
|
||||||
services:
|
services:
|
||||||
api: &api-environment
|
api:
|
||||||
image: jhumanj/opnform-api:latest
|
image: jhumanj/opnform-api:latest
|
||||||
container_name: opnform-api
|
container_name: opnform-api
|
||||||
volumes: &api-environment-volumes
|
volumes:
|
||||||
- opnform_storage:/usr/share/nginx/html/storage:rw
|
- opnform_storage:/usr/share/nginx/html/storage:rw
|
||||||
environment: &api-env
|
environment:
|
||||||
APP_ENV: production
|
APP_ENV: production
|
||||||
# Database settings
|
# Database settings
|
||||||
DB_HOST: db
|
DB_HOST: db
|
||||||
|
|
@ -25,7 +24,7 @@ services:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy # Depend on redis being healthy too
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan about || exit 1"]
|
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan about || exit 1"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
|
@ -33,13 +32,49 @@ services:
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 60s
|
start_period: 60s
|
||||||
|
|
||||||
|
api-nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: opnform-api-nginx
|
||||||
|
volumes:
|
||||||
|
- ./docker/api-nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- opnform_storage:/usr/share/nginx/html/storage:ro
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:7654:80" # API on port 7654
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget --spider -q http://localhost/ || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
api-worker:
|
api-worker:
|
||||||
<<: *api-environment
|
image: jhumanj/opnform-api:latest
|
||||||
container_name: opnform-api-worker
|
container_name: opnform-api-worker
|
||||||
command: ["php", "artisan", "queue:work"]
|
command: ["php", "artisan", "queue:work"]
|
||||||
|
volumes:
|
||||||
|
- opnform_storage:/usr/share/nginx/html/storage:rw
|
||||||
environment:
|
environment:
|
||||||
<<: *api-env
|
|
||||||
APP_ENV: production
|
APP_ENV: production
|
||||||
|
# Database settings
|
||||||
|
DB_HOST: db
|
||||||
|
REDIS_HOST: redis
|
||||||
|
DB_DATABASE: ${DB_DATABASE:-forge}
|
||||||
|
DB_USERNAME: ${DB_USERNAME:-forge}
|
||||||
|
DB_PASSWORD: ${DB_PASSWORD:-forge}
|
||||||
|
DB_CONNECTION: ${DB_CONNECTION:-pgsql}
|
||||||
|
# PHP Configuration
|
||||||
|
PHP_MEMORY_LIMIT: "1G"
|
||||||
|
PHP_MAX_EXECUTION_TIME: "600"
|
||||||
|
PHP_UPLOAD_MAX_FILESIZE: "64M"
|
||||||
|
PHP_POST_MAX_SIZE: "64M"
|
||||||
|
env_file:
|
||||||
|
- ./api/.env
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pgrep -f 'php artisan queue:work' > /dev/null || exit 1"]
|
test: ["CMD-SHELL", "pgrep -f 'php artisan queue:work' > /dev/null || exit 1"]
|
||||||
interval: 60s
|
interval: 60s
|
||||||
|
|
@ -48,22 +83,44 @@ services:
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
api-scheduler:
|
api-scheduler:
|
||||||
<<: *api-environment
|
image: jhumanj/opnform-api:latest
|
||||||
container_name: opnform-api-scheduler
|
container_name: opnform-api-scheduler
|
||||||
command: ["php", "artisan", "schedule:work"]
|
command: ["php", "artisan", "schedule:work"]
|
||||||
|
volumes:
|
||||||
|
- opnform_storage:/usr/share/nginx/html/storage:rw
|
||||||
environment:
|
environment:
|
||||||
<<: *api-env
|
|
||||||
APP_ENV: production
|
APP_ENV: production
|
||||||
|
# Database settings
|
||||||
|
DB_HOST: db
|
||||||
|
REDIS_HOST: redis
|
||||||
|
DB_DATABASE: ${DB_DATABASE:-forge}
|
||||||
|
DB_USERNAME: ${DB_USERNAME:-forge}
|
||||||
|
DB_PASSWORD: ${DB_PASSWORD:-forge}
|
||||||
|
DB_CONNECTION: ${DB_CONNECTION:-pgsql}
|
||||||
|
# PHP Configuration
|
||||||
|
PHP_MEMORY_LIMIT: "1G"
|
||||||
|
PHP_MAX_EXECUTION_TIME: "600"
|
||||||
|
PHP_UPLOAD_MAX_FILESIZE: "64M"
|
||||||
|
PHP_POST_MAX_SIZE: "64M"
|
||||||
|
env_file:
|
||||||
|
- ./api/.env
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan app:scheduler-status --mode=check --max-minutes=3 || exit 1"]
|
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan app:scheduler-status --mode=check --max-minutes=3 || exit 1"]
|
||||||
interval: 60s
|
interval: 60s
|
||||||
timeout: 30s
|
timeout: 30s
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 70s # Allow time for first scheduled run and cache write
|
start_period: 70s
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
image: jhumanj/opnform-client:latest
|
image: jhumanj/opnform-client:latest
|
||||||
container_name: opnform-client
|
container_name: opnform-client
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:7655:3000" # UI on port 7655
|
||||||
env_file:
|
env_file:
|
||||||
- ./client/.env
|
- ./client/.env
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|
@ -95,27 +152,6 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- postgres-data:/var/lib/postgresql/data
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
|
||||||
ingress:
|
|
||||||
image: nginx:1
|
|
||||||
container_name: opnform-ingress
|
|
||||||
volumes:
|
|
||||||
- ./docker/nginx.conf:/etc/nginx/templates/default.conf.template
|
|
||||||
ports:
|
|
||||||
- 80:80
|
|
||||||
environment:
|
|
||||||
- NGINX_MAX_BODY_SIZE=64m
|
|
||||||
depends_on:
|
|
||||||
api:
|
|
||||||
condition: service_started
|
|
||||||
ui:
|
|
||||||
condition: service_started
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "nginx -t && curl -f http://localhost/ || exit 1"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres-data:
|
postgres-data:
|
||||||
opnform_storage:
|
opnform_storage:
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ RUN composer install --optimize-autoloader --no-interaction \
|
||||||
# Final stage - smaller runtime image
|
# Final stage - smaller runtime image
|
||||||
FROM php:8.3-fpm-alpine
|
FROM php:8.3-fpm-alpine
|
||||||
|
|
||||||
|
# Accept version build argument
|
||||||
|
ARG APP_VERSION=unknown
|
||||||
|
|
||||||
# Install runtime dependencies
|
# Install runtime dependencies
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
libzip \
|
libzip \
|
||||||
|
|
@ -75,6 +78,9 @@ RUN mkdir -p storage/framework/sessions \
|
||||||
# Copy the entire application from the builder stage
|
# Copy the entire application from the builder stage
|
||||||
COPY --from=builder /app/ ./
|
COPY --from=builder /app/ ./
|
||||||
|
|
||||||
|
# Set version as environment variable (more reliable than file approach)
|
||||||
|
ENV APP_VERSION_DOCKER=$APP_VERSION
|
||||||
|
|
||||||
# Setup entrypoint
|
# Setup entrypoint
|
||||||
COPY docker/php-fpm-entrypoint /usr/local/bin/opnform-entrypoint
|
COPY docker/php-fpm-entrypoint /usr/local/bin/opnform-entrypoint
|
||||||
RUN chmod a+x /usr/local/bin/*
|
RUN chmod a+x /usr/local/bin/*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
access_log /dev/stdout;
|
||||||
|
error_log /dev/stderr;
|
||||||
|
|
||||||
|
# Handle all requests through PHP
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
# PHP-FPM configuration
|
||||||
|
location ~ \.php$ {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass opnform-api:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php;
|
||||||
|
fastcgi_param DOCUMENT_ROOT /usr/share/nginx/html/public;
|
||||||
|
fastcgi_param REQUEST_URI $request_uri;
|
||||||
|
fastcgi_read_timeout 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deny access to . files
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Example nginx configuration for forms.portnimara.dev
|
||||||
|
# Place this in /etc/nginx/sites-available/forms.portnimara.dev
|
||||||
|
# Then create a symlink: ln -s /etc/nginx/sites-available/forms.portnimara.dev /etc/nginx/sites-enabled/
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name forms.portnimara.dev;
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name forms.portnimara.dev;
|
||||||
|
|
||||||
|
# SSL certificates - adjust paths as needed
|
||||||
|
ssl_certificate /etc/letsencrypt/live/forms.portnimara.dev/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/forms.portnimara.dev/privkey.pem;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# Client upload size
|
||||||
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
access_log /var/log/nginx/forms.portnimara.dev.access.log;
|
||||||
|
error_log /var/log/nginx/forms.portnimara.dev.error.log;
|
||||||
|
|
||||||
|
# API routes - proxy to the api-nginx container
|
||||||
|
location ~ ^/(api|open|local/temp|forms/assets)/ {
|
||||||
|
proxy_pass http://127.0.0.1:7654;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
proxy_connect_timeout 75s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Everything else goes to the UI container
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:7655;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
|
||||||
|
# WebSocket support for hot reload and real-time features
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
}
|
||||||
|
|
@ -63,9 +63,12 @@ if [ "$DEV_MODE" = true ]; then
|
||||||
else
|
else
|
||||||
echo -e "${BLUE}Production environment setup complete!${NC}"
|
echo -e "${BLUE}Production environment setup complete!${NC}"
|
||||||
echo -e "${YELLOW}Please wait a moment for all services to start${NC}"
|
echo -e "${YELLOW}Please wait a moment for all services to start${NC}"
|
||||||
echo -e "${GREEN}Then visit: http://localhost${NC}"
|
echo -e "${GREEN}Services are available on:${NC}"
|
||||||
|
echo -e "${GREEN}- UI: http://localhost:7655${NC}"
|
||||||
|
echo -e "${GREEN}- API: http://localhost:7654${NC}"
|
||||||
|
echo -e "${YELLOW}Note: Configure your host nginx to proxy to these ports${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${BLUE}Default admin credentials:${NC}"
|
echo -e "${BLUE}Default admin credentials:${NC}"
|
||||||
echo -e "${GREEN}Email: admin@opnform.com${NC}"
|
echo -e "${GREEN}Email: admin@opnform.com${NC}"
|
||||||
echo -e "${GREEN}Password: password${NC}"
|
echo -e "${GREEN}Password: password${NC}"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue