From 978b072bff1b660464a887c9e603c5f8752b26fb Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 28 Jan 2025 22:26:30 -0800 Subject: [PATCH 01/37] Enhancement: improve doc details close behavior (#8937) --- src-ui/src/app/app.component.ts | 4 +- .../document-detail.component.spec.ts | 13 +++ .../document-detail.component.ts | 8 +- .../services/component-router.service.spec.ts | 102 ++++++++++++++++++ .../app/services/component-router.service.ts | 35 ++++++ 5 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 src-ui/src/app/services/component-router.service.spec.ts create mode 100644 src-ui/src/app/services/component-router.service.ts diff --git a/src-ui/src/app/app.component.ts b/src-ui/src/app/app.component.ts index d22b4ca38..c89f5d4c2 100644 --- a/src-ui/src/app/app.component.ts +++ b/src-ui/src/app/app.component.ts @@ -5,6 +5,7 @@ import { first, Subscription } from 'rxjs' import { ToastsComponent } from './components/common/toasts/toasts.component' import { FileDropComponent } from './components/file-drop/file-drop.component' import { SETTINGS_KEYS } from './data/ui-settings' +import { ComponentRouterService } from './services/component-router.service' import { ConsumerStatusService } from './services/consumer-status.service' import { HotKeyService } from './services/hot-key.service' import { @@ -41,7 +42,8 @@ export class AppComponent implements OnInit, OnDestroy { public tourService: TourService, private renderer: Renderer2, private permissionsService: PermissionsService, - private hotKeyService: HotKeyService + private hotKeyService: HotKeyService, + private componentRouterService: ComponentRouterService ) { let anyWindow = window as any anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs' diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index fb2596d4a..0a2e5605f 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -45,6 +45,7 @@ import { Tag } from 'src/app/data/tag' import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' +import { ComponentRouterService } from 'src/app/services/component-router.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { OpenDocumentsService } from 'src/app/services/open-documents.service' import { PermissionsService } from 'src/app/services/permissions.service' @@ -127,6 +128,7 @@ describe('DocumentDetailComponent', () => { let settingsService: SettingsService let customFieldsService: CustomFieldsService let httpTestingController: HttpTestingController + let componentRouterService: ComponentRouterService let currentUserCan = true let currentUserHasObjectPermissions = true @@ -264,6 +266,7 @@ describe('DocumentDetailComponent', () => { customFieldsService = TestBed.inject(CustomFieldsService) fixture = TestBed.createComponent(DocumentDetailComponent) httpTestingController = TestBed.inject(HttpTestingController) + componentRouterService = TestBed.inject(ComponentRouterService) component = fixture.componentInstance }) @@ -568,6 +571,16 @@ describe('DocumentDetailComponent', () => { expect(navigateSpy).toHaveBeenCalledWith(['documents']) }) + it('should allow close and navigate to the last view if available', () => { + initNormally() + jest + .spyOn(componentRouterService, 'getComponentURLBefore') + .mockReturnValue('dashboard') + const navigateSpy = jest.spyOn(router, 'navigate') + component.close() + expect(navigateSpy).toHaveBeenCalledWith(['dashboard']) + }) + it('should allow close and navigate to documents by default', () => { initNormally() jest diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 6b65ad335..8d1b35071 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -59,6 +59,7 @@ import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' import { FileSizePipe } from 'src/app/pipes/file-size.pipe' import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe' +import { ComponentRouterService } from 'src/app/services/component-router.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { HotKeyService } from 'src/app/services/hot-key.service' import { OpenDocumentsService } from 'src/app/services/open-documents.service' @@ -272,7 +273,8 @@ export class DocumentDetailComponent private userService: UserService, private customFieldsService: CustomFieldsService, private http: HttpClient, - private hotKeyService: HotKeyService + private hotKeyService: HotKeyService, + private componentRouterService: ComponentRouterService ) { super() } @@ -888,6 +890,10 @@ export class DocumentDetailComponent 'view', this.documentListViewService.activeSavedViewId, ]) + } else if (this.componentRouterService.getComponentURLBefore()) { + this.router.navigate([ + this.componentRouterService.getComponentURLBefore(), + ]) } else { this.router.navigate(['documents']) } diff --git a/src-ui/src/app/services/component-router.service.spec.ts b/src-ui/src/app/services/component-router.service.spec.ts new file mode 100644 index 000000000..b11fc8197 --- /dev/null +++ b/src-ui/src/app/services/component-router.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed } from '@angular/core/testing' +import { ActivationStart, Router } from '@angular/router' +import { Subject } from 'rxjs' +import { ComponentRouterService } from './component-router.service' + +describe('ComponentRouterService', () => { + let service: ComponentRouterService + let router: Router + let eventsSubject: Subject + + beforeEach(() => { + eventsSubject = new Subject() + TestBed.configureTestingModule({ + providers: [ + ComponentRouterService, + { + provide: Router, + useValue: { + events: eventsSubject.asObservable(), + }, + }, + ], + }) + service = TestBed.inject(ComponentRouterService) + router = TestBed.inject(Router) + }) + + it('should add to history and componentHistory on ActivationStart event', () => { + eventsSubject.next( + new ActivationStart({ + url: 'test-url', + component: { name: 'TestComponent' }, + } as any) + ) + + expect((service as any).history).toEqual(['test-url']) + expect((service as any).componentHistory).toEqual(['TestComponent']) + }) + + it('should not add duplicate component names to componentHistory', () => { + eventsSubject.next( + new ActivationStart({ + url: 'test-url-1', + component: { name: 'TestComponent' }, + } as any) + ) + eventsSubject.next( + new ActivationStart({ + url: 'test-url-2', + component: { name: 'TestComponent' }, + } as any) + ) + + expect((service as any).componentHistory.length).toBe(1) + expect((service as any).componentHistory).toEqual(['TestComponent']) + }) + + it('should return the URL of the component before the current one', () => { + eventsSubject.next( + new ActivationStart({ + url: 'test-url-1', + component: { name: 'TestComponent1' }, + } as any) + ) + eventsSubject.next( + new ActivationStart({ + url: 'test-url-2', + component: { name: 'TestComponent2' }, + } as any) + ) + + expect(service.getComponentURLBefore()).toBe('test-url-1') + }) + + it('should update the URL of the current component if the same component is loaded via a different URL', () => { + eventsSubject.next( + new ActivationStart({ + url: 'test-url-1', + component: { name: 'TestComponent' }, + } as any) + ) + eventsSubject.next( + new ActivationStart({ + url: 'test-url-2', + component: { name: 'TestComponent' }, + } as any) + ) + + expect((service as any).history).toEqual(['test-url-2']) + }) + + it('should return null if there is no previous component', () => { + eventsSubject.next( + new ActivationStart({ + url: 'test-url', + component: { name: 'TestComponent' }, + } as any) + ) + + expect(service.getComponentURLBefore()).toBeNull() + }) +}) diff --git a/src-ui/src/app/services/component-router.service.ts b/src-ui/src/app/services/component-router.service.ts new file mode 100644 index 000000000..3f97584b7 --- /dev/null +++ b/src-ui/src/app/services/component-router.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core' +import { ActivationStart, Event, Router } from '@angular/router' +import { filter } from 'rxjs' + +@Injectable({ + providedIn: 'root', +}) +export class ComponentRouterService { + private history: string[] = [] + private componentHistory: any[] = [] + + constructor(private router: Router) { + this.router.events + .pipe(filter((event: Event) => event instanceof ActivationStart)) + .subscribe((event: ActivationStart) => { + if ( + this.componentHistory[this.componentHistory.length - 1] !== + event.snapshot.component.name + ) { + this.history.push(event.snapshot.url.toString()) + this.componentHistory.push(event.snapshot.component.name) + } else { + // Update the URL of the current component in case the same component was loaded via a different URL + this.history[this.history.length - 1] = event.snapshot.url.toString() + } + }) + } + + public getComponentURLBefore(): any { + if (this.componentHistory.length > 1) { + return this.history[this.history.length - 2] + } + return null + } +} From 79956d6a7ba3b2571f2d5c5e6b738c4598dd6eb6 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 29 Jan 2025 07:23:44 -0800 Subject: [PATCH 02/37] Enhancement: require totp code for obtain auth token (#8936) --- src/documents/tests/test_api_permissions.py | 66 +++++++++++++++++++++ src/paperless/serialisers.py | 33 +++++++++++ src/paperless/urls.py | 4 +- src/paperless/views.py | 6 ++ 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/src/documents/tests/test_api_permissions.py b/src/documents/tests/test_api_permissions.py index 5de1887b2..3785c8f2a 100644 --- a/src/documents/tests/test_api_permissions.py +++ b/src/documents/tests/test_api_permissions.py @@ -3,6 +3,7 @@ import json from unittest import mock from allauth.mfa.models import Authenticator +from allauth.mfa.totp.internal import auth as totp_auth from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.auth.models import User @@ -488,6 +489,71 @@ class TestApiAuth(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response.data["detail"], "MFA required") + @mock.patch("allauth.mfa.totp.internal.auth.TOTP.validate_code") + def test_get_token_mfa_enabled(self, mock_validate_code): + """ + GIVEN: + - User with MFA enabled + WHEN: + - API request is made to obtain an auth token + THEN: + - MFA code is required + """ + user1 = User.objects.create_user(username="user1") + user1.set_password("password") + user1.save() + + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + }, + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + secret = totp_auth.generate_totp_secret() + totp_auth.TOTP.activate( + user1, + secret, + ) + + # no code + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + }, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data["non_field_errors"][0], "MFA code is required") + + # invalid code + mock_validate_code.return_value = False + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + "code": "123456", + }, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data["non_field_errors"][0], "Invalid MFA code") + + # valid code + mock_validate_code.return_value = True + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + "code": "123456", + }, + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + class TestApiUser(DirectoriesMixin, APITestCase): ENDPOINT = "/api/users/" diff --git a/src/paperless/serialisers.py b/src/paperless/serialisers.py index d5acfe465..fb1f511f7 100644 --- a/src/paperless/serialisers.py +++ b/src/paperless/serialisers.py @@ -1,11 +1,14 @@ import logging from allauth.mfa.adapter import get_adapter as get_mfa_adapter +from allauth.mfa.models import Authenticator +from allauth.mfa.totp.internal.auth import TOTP from allauth.socialaccount.models import SocialAccount from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.auth.models import User from rest_framework import serializers +from rest_framework.authtoken.serializers import AuthTokenSerializer from paperless.models import ApplicationConfiguration @@ -24,6 +27,36 @@ class ObfuscatedUserPasswordField(serializers.Field): return data +class PaperlessAuthTokenSerializer(AuthTokenSerializer): + code = serializers.CharField( + label="MFA Code", + write_only=True, + required=False, + ) + + def validate(self, attrs): + attrs = super().validate(attrs) + user = attrs.get("user") + code = attrs.get("code") + mfa_adapter = get_mfa_adapter() + if mfa_adapter.is_mfa_enabled(user): + if not code: + raise serializers.ValidationError( + "MFA code is required", + ) + authenticator = Authenticator.objects.get( + user=user, + type=Authenticator.Type.TOTP, + ) + if not TOTP(instance=authenticator).validate_code( + code, + ): + raise serializers.ValidationError( + "Invalid MFA code", + ) + return attrs + + class UserSerializer(serializers.ModelSerializer): password = ObfuscatedUserPasswordField(required=False) user_permissions = serializers.SlugRelatedField( diff --git a/src/paperless/urls.py b/src/paperless/urls.py index c528c5e2a..703a72042 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -14,7 +14,6 @@ from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic import RedirectView from django.views.static import serve -from rest_framework.authtoken import views from rest_framework.routers import DefaultRouter from documents.views import BulkDownloadView @@ -50,6 +49,7 @@ from paperless.views import DisconnectSocialAccountView from paperless.views import FaviconView from paperless.views import GenerateAuthTokenView from paperless.views import GroupViewSet +from paperless.views import PaperlessObtainAuthTokenView from paperless.views import ProfileView from paperless.views import SocialAccountProvidersView from paperless.views import TOTPView @@ -157,7 +157,7 @@ urlpatterns = [ ), path( "token/", - views.obtain_auth_token, + PaperlessObtainAuthTokenView.as_view(), ), re_path( "^profile/", diff --git a/src/paperless/views.py b/src/paperless/views.py index 03721adf2..bcabd182f 100644 --- a/src/paperless/views.py +++ b/src/paperless/views.py @@ -19,6 +19,7 @@ from django.http import HttpResponseNotFound from django.views.generic import View from django_filters.rest_framework import DjangoFilterBackend from rest_framework.authtoken.models import Token +from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.decorators import action from rest_framework.filters import OrderingFilter from rest_framework.generics import GenericAPIView @@ -35,10 +36,15 @@ from paperless.filters import UserFilterSet from paperless.models import ApplicationConfiguration from paperless.serialisers import ApplicationConfigurationSerializer from paperless.serialisers import GroupSerializer +from paperless.serialisers import PaperlessAuthTokenSerializer from paperless.serialisers import ProfileSerializer from paperless.serialisers import UserSerializer +class PaperlessObtainAuthTokenView(ObtainAuthToken): + serializer_class = PaperlessAuthTokenSerializer + + class StandardPagination(PageNumberPagination): page_size = 25 page_size_query_param = "page_size" From fce7b033247ba7611fca3ec3f3afa2fdd72aa297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Steinbei=C3=9Fer?= <33968289+gothicVI@users.noreply.github.com> Date: Wed, 29 Jan 2025 19:58:53 +0100 Subject: [PATCH 03/37] Chore: Switch from os.path to pathlib.Path (#8644) --- .ruff.toml | 3 - src/documents/consumer.py | 36 +++-- src/documents/signals/handlers.py | 2 +- src/documents/tests/test_api_bulk_download.py | 24 +-- src/documents/tests/test_api_documents.py | 144 ++++++------------ 5 files changed, 72 insertions(+), 137 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index 96ee7430b..d9ca6b321 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -38,7 +38,6 @@ ignore = ["DJ001", "SIM105", "RUF012"] [lint.per-file-ignores] ".github/scripts/*.py" = ["E501", "INP001", "SIM117"] "docker/wait-for-redis.py" = ["INP001", "T201"] -"src/documents/consumer.py" = ["PTH"] # TODO Enable & remove "src/documents/file_handling.py" = ["PTH"] # TODO Enable & remove "src/documents/management/commands/document_consumer.py" = ["PTH"] # TODO Enable & remove "src/documents/management/commands/document_exporter.py" = ["PTH"] # TODO Enable & remove @@ -51,8 +50,6 @@ ignore = ["DJ001", "SIM105", "RUF012"] "src/documents/signals/handlers.py" = ["PTH"] # TODO Enable & remove "src/documents/tasks.py" = ["PTH"] # TODO Enable & remove "src/documents/tests/test_api_app_config.py" = ["PTH"] # TODO Enable & remove -"src/documents/tests/test_api_bulk_download.py" = ["PTH"] # TODO Enable & remove -"src/documents/tests/test_api_documents.py" = ["PTH"] # TODO Enable & remove "src/documents/tests/test_classifier.py" = ["PTH"] # TODO Enable & remove "src/documents/tests/test_consumer.py" = ["PTH"] # TODO Enable & remove "src/documents/tests/test_file_handling.py" = ["PTH"] # TODO Enable & remove diff --git a/src/documents/consumer.py b/src/documents/consumer.py index ec92ddba8..35c18ac7b 100644 --- a/src/documents/consumer.py +++ b/src/documents/consumer.py @@ -4,6 +4,7 @@ import os import tempfile from enum import Enum from pathlib import Path +from typing import TYPE_CHECKING import magic from django.conf import settings @@ -154,7 +155,11 @@ class ConsumerPlugin( """ Confirm the input file still exists where it should """ - if not os.path.isfile(self.input_doc.original_file): + if TYPE_CHECKING: + assert isinstance(self.input_doc.original_file, Path), ( + self.input_doc.original_file + ) + if not self.input_doc.original_file.is_file(): self._fail( ConsumerStatusShortMessage.FILE_NOT_FOUND, f"Cannot consume {self.input_doc.original_file}: File not found.", @@ -164,7 +169,7 @@ class ConsumerPlugin( """ Using the MD5 of the file, check this exact file doesn't already exist """ - with open(self.input_doc.original_file, "rb") as f: + with Path(self.input_doc.original_file).open("rb") as f: checksum = hashlib.md5(f.read()).hexdigest() existing_doc = Document.global_objects.filter( Q(checksum=checksum) | Q(archive_checksum=checksum), @@ -178,7 +183,7 @@ class ConsumerPlugin( log_msg += " Note: existing document is in the trash." if settings.CONSUMER_DELETE_DUPLICATES: - os.unlink(self.input_doc.original_file) + Path(self.input_doc.original_file).unlink() self._fail( msg, log_msg, @@ -237,7 +242,7 @@ class ConsumerPlugin( if not settings.PRE_CONSUME_SCRIPT: return - if not os.path.isfile(settings.PRE_CONSUME_SCRIPT): + if not Path(settings.PRE_CONSUME_SCRIPT).is_file(): self._fail( ConsumerStatusShortMessage.PRE_CONSUME_SCRIPT_NOT_FOUND, f"Configured pre-consume script " @@ -280,7 +285,7 @@ class ConsumerPlugin( if not settings.POST_CONSUME_SCRIPT: return - if not os.path.isfile(settings.POST_CONSUME_SCRIPT): + if not Path(settings.POST_CONSUME_SCRIPT).is_file(): self._fail( ConsumerStatusShortMessage.POST_CONSUME_SCRIPT_NOT_FOUND, f"Configured post-consume script " @@ -582,7 +587,7 @@ class ConsumerPlugin( document.thumbnail_path, ) - if archive_path and os.path.isfile(archive_path): + if archive_path and Path(archive_path).is_file(): document.archive_filename = generate_unique_filename( document, archive_filename=True, @@ -594,7 +599,7 @@ class ConsumerPlugin( document.archive_path, ) - with open(archive_path, "rb") as f: + with Path(archive_path).open("rb") as f: document.archive_checksum = hashlib.md5( f.read(), ).hexdigest() @@ -612,14 +617,14 @@ class ConsumerPlugin( self.unmodified_original.unlink() # https://github.com/jonaswinkler/paperless-ng/discussions/1037 - shadow_file = os.path.join( - os.path.dirname(self.input_doc.original_file), - "._" + os.path.basename(self.input_doc.original_file), + shadow_file = ( + Path(self.input_doc.original_file).parent + / f"._{Path(self.input_doc.original_file).name}" ) - if os.path.isfile(shadow_file): + if Path(shadow_file).is_file(): self.log.debug(f"Deleting file {shadow_file}") - os.unlink(shadow_file) + Path(shadow_file).unlink() except Exception as e: self._fail( @@ -704,7 +709,7 @@ class ConsumerPlugin( create_date = date self.log.debug(f"Creation date from parse_date: {create_date}") else: - stats = os.stat(self.input_doc.original_file) + stats = Path(self.input_doc.original_file).stat() create_date = timezone.make_aware( datetime.datetime.fromtimestamp(stats.st_mtime), ) @@ -800,7 +805,10 @@ class ConsumerPlugin( ) # adds to document def _write(self, storage_type, source, target): - with open(source, "rb") as read_file, open(target, "wb") as write_file: + with ( + Path(source).open("rb") as read_file, + Path(target).open("wb") as write_file, + ): write_file.write(read_file.read()) # Attempt to copy file's original stats, but it's ok if we can't diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index e60585a37..4885910fd 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -353,7 +353,7 @@ def cleanup_document_deletion(sender, instance, **kwargs): f"{filename} could not be deleted: {e}", ) elif filename and not os.path.isfile(filename): - logger.warn(f"Expected {filename} tp exist, but it did not") + logger.warning(f"Expected {filename} to exist, but it did not") delete_empty_directories( os.path.dirname(instance.source_path), diff --git a/src/documents/tests/test_api_bulk_download.py b/src/documents/tests/test_api_bulk_download.py index 31bf182b5..a7e8f5df3 100644 --- a/src/documents/tests/test_api_bulk_download.py +++ b/src/documents/tests/test_api_bulk_download.py @@ -1,7 +1,6 @@ import datetime import io import json -import os import shutil import zipfile @@ -15,9 +14,10 @@ from documents.models import Correspondent from documents.models import Document from documents.models import DocumentType from documents.tests.utils import DirectoriesMixin +from documents.tests.utils import SampleDirMixin -class TestBulkDownload(DirectoriesMixin, APITestCase): +class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase): ENDPOINT = "/api/documents/bulk_download/" def setUp(self): @@ -51,22 +51,10 @@ class TestBulkDownload(DirectoriesMixin, APITestCase): archive_checksum="D", ) - shutil.copy( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - self.doc2.source_path, - ) - shutil.copy( - os.path.join(os.path.dirname(__file__), "samples", "simple.png"), - self.doc2b.source_path, - ) - shutil.copy( - os.path.join(os.path.dirname(__file__), "samples", "simple.jpg"), - self.doc3.source_path, - ) - shutil.copy( - os.path.join(os.path.dirname(__file__), "samples", "test_with_bom.pdf"), - self.doc3.archive_path, - ) + shutil.copy(self.SAMPLE_DIR / "simple.pdf", self.doc2.source_path) + shutil.copy(self.SAMPLE_DIR / "simple.png", self.doc2b.source_path) + shutil.copy(self.SAMPLE_DIR / "simple.jpg", self.doc3.source_path) + shutil.copy(self.SAMPLE_DIR / "test_with_bom.pdf", self.doc3.archive_path) def test_download_originals(self): response = self.client.post( diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index 70db15217..b7a4f4e2f 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -1,5 +1,4 @@ import datetime -import os import shutil import tempfile import uuid @@ -8,6 +7,7 @@ from binascii import hexlify from datetime import date from datetime import timedelta from pathlib import Path +from typing import TYPE_CHECKING from unittest import mock import celery @@ -171,19 +171,18 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): content = b"This is a test" content_thumbnail = b"thumbnail content" - with open(filename, "wb") as f: + with Path(filename).open("wb") as f: f.write(content) doc = Document.objects.create( title="none", - filename=os.path.basename(filename), + filename=Path(filename).name, mime_type="application/pdf", ) - with open( - os.path.join(self.dirs.thumbnail_dir, f"{doc.pk:07d}.webp"), - "wb", - ) as f: + if TYPE_CHECKING: + assert isinstance(self.dirs.thumbnail_dir, Path), self.dirs.thumbnail_dir + with (self.dirs.thumbnail_dir / f"{doc.pk:07d}.webp").open("wb") as f: f.write(content_thumbnail) response = self.client.get(f"/api/documents/{doc.pk}/download/") @@ -217,7 +216,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): content = b"This is a test" content_thumbnail = b"thumbnail content" - with open(filename, "wb") as f: + with Path(filename).open("wb") as f: f.write(content) user1 = User.objects.create_user(username="test1") @@ -229,15 +228,12 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): doc = Document.objects.create( title="none", - filename=os.path.basename(filename), + filename=Path(filename).name, mime_type="application/pdf", owner=user1, ) - with open( - os.path.join(self.dirs.thumbnail_dir, f"{doc.pk:07d}.webp"), - "wb", - ) as f: + with (Path(self.dirs.thumbnail_dir) / f"{doc.pk:07d}.webp").open("wb") as f: f.write(content_thumbnail) response = self.client.get(f"/api/documents/{doc.pk}/download/") @@ -272,10 +268,10 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): mime_type="application/pdf", ) - with open(doc.source_path, "wb") as f: + with Path(doc.source_path).open("wb") as f: f.write(content) - with open(doc.archive_path, "wb") as f: + with Path(doc.archive_path).open("wb") as f: f.write(content_archive) response = self.client.get(f"/api/documents/{doc.pk}/download/") @@ -305,7 +301,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): def test_document_actions_not_existing_file(self): doc = Document.objects.create( title="none", - filename=os.path.basename("asd"), + filename=Path("asd").name, mime_type="application/pdf", ) @@ -1026,10 +1022,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f}, @@ -1061,10 +1054,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", { @@ -1095,10 +1085,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"documenst": f}, @@ -1111,10 +1098,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.zip"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.zip").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f}, @@ -1127,10 +1111,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "title": "my custom title"}, @@ -1152,10 +1133,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): ) c = Correspondent.objects.create(name="test-corres") - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "correspondent": c.id}, @@ -1176,10 +1154,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "correspondent": 3456}, @@ -1194,10 +1169,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): ) dt = DocumentType.objects.create(name="invoice") - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "document_type": dt.id}, @@ -1218,10 +1190,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "document_type": 34578}, @@ -1236,10 +1205,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): ) sp = StoragePath.objects.create(name="invoices") - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "storage_path": sp.id}, @@ -1260,10 +1226,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "storage_path": 34578}, @@ -1279,10 +1242,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): t1 = Tag.objects.create(name="tag1") t2 = Tag.objects.create(name="tag2") - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "tags": [t2.id, t1.id]}, @@ -1305,10 +1265,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): t1 = Tag.objects.create(name="tag1") t2 = Tag.objects.create(name="tag2") - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "tags": [t2.id, t1.id, 734563]}, @@ -1332,10 +1289,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): 0, tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles"), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "created": created}, @@ -1353,10 +1307,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f, "archive_serial_number": 500}, @@ -1385,10 +1336,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): data_type=CustomField.FieldDataType.STRING, ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", { @@ -1417,10 +1365,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): id=str(uuid.uuid4()), ) - with open( - os.path.join(os.path.dirname(__file__), "samples", "invalid_pdf.pdf"), - "rb", - ) as f: + with (Path(__file__).parent / "samples" / "invalid_pdf.pdf").open("rb") as f: response = self.client.post( "/api/documents/post_document/", {"document": f}, @@ -1437,14 +1382,14 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): archive_filename="archive.pdf", ) - source_file = os.path.join( - os.path.dirname(__file__), - "samples", - "documents", - "thumbnails", - "0000001.webp", + source_file: Path = ( + Path(__file__).parent + / "samples" + / "documents" + / "thumbnails" + / "0000001.webp" ) - archive_file = os.path.join(os.path.dirname(__file__), "samples", "simple.pdf") + archive_file: Path = Path(__file__).parent / "samples" / "simple.pdf" shutil.copy(source_file, doc.source_path) shutil.copy(archive_file, doc.archive_path) @@ -1460,8 +1405,8 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): self.assertGreater(len(meta["archive_metadata"]), 0) self.assertEqual(meta["media_filename"], "file.pdf") self.assertEqual(meta["archive_media_filename"], "archive.pdf") - self.assertEqual(meta["original_size"], os.stat(source_file).st_size) - self.assertEqual(meta["archive_size"], os.stat(archive_file).st_size) + self.assertEqual(meta["original_size"], Path(source_file).stat().st_size) + self.assertEqual(meta["archive_size"], Path(archive_file).stat().st_size) response = self.client.get(f"/api/documents/{doc.pk}/metadata/") self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -1477,10 +1422,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): mime_type="application/pdf", ) - shutil.copy( - os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), - doc.source_path, - ) + shutil.copy(Path(__file__).parent / "samples" / "simple.pdf", doc.source_path) response = self.client.get(f"/api/documents/{doc.pk}/metadata/") self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -1939,9 +1881,9 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): def test_get_logs(self): log_data = "test\ntest2\n" - with open(os.path.join(settings.LOGGING_DIR, "mail.log"), "w") as f: + with (Path(settings.LOGGING_DIR) / "mail.log").open("w") as f: f.write(log_data) - with open(os.path.join(settings.LOGGING_DIR, "paperless.log"), "w") as f: + with (Path(settings.LOGGING_DIR) / "paperless.log").open("w") as f: f.write(log_data) response = self.client.get("/api/logs/") self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -1949,7 +1891,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): def test_get_logs_only_when_exist(self): log_data = "test\ntest2\n" - with open(os.path.join(settings.LOGGING_DIR, "paperless.log"), "w") as f: + with (Path(settings.LOGGING_DIR) / "paperless.log").open("w") as f: f.write(log_data) response = self.client.get("/api/logs/") self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -1966,7 +1908,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): def test_get_log(self): log_data = "test\ntest2\n" - with open(os.path.join(settings.LOGGING_DIR, "paperless.log"), "w") as f: + with (Path(settings.LOGGING_DIR) / "paperless.log").open("w") as f: f.write(log_data) response = self.client.get("/api/logs/paperless/") self.assertEqual(response.status_code, status.HTTP_200_OK) From 311b259cff4e994b6b45fb768205e3a27b2d1d3e Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Wed, 29 Jan 2025 13:00:49 -0800 Subject: [PATCH 04/37] Chore: Updates the curl command to download in parallel (#8945) --- Dockerfile | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index d3be53dff..c8fdcf62d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -127,32 +127,21 @@ RUN set -eux \ && apt-get update \ && apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} \ && echo "Installing pre-built updates" \ + && curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \ + https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \ + https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \ + https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ + https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ + https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \ + https://github.com/paperless-ngx/builder/releases/download/jbig2enc-${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \ && echo "Installing qpdf ${QPDF_VERSION}" \ - && curl --fail --silent --show-error --location \ - --output libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \ - https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \ - && curl --fail --silent --show-error --location \ - --output qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \ - https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \ && dpkg --install ./libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \ && dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \ && echo "Installing Ghostscript ${GS_VERSION}" \ - && curl --fail --silent --show-error --location \ - --output libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ - https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ - && curl --fail --silent --show-error --location \ - --output ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ - https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ - && curl --fail --silent --show-error --location \ - --output libgs10-common_${GS_VERSION}.dfsg-1_all.deb \ - https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \ && dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-1_all.deb \ && dpkg --install ./libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ && dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ && echo "Installing jbig2enc" \ - && curl --fail --silent --show-error --location \ - --output jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \ - https://github.com/paperless-ngx/builder/releases/download/jbig2enc-${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \ && dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \ && echo "Cleaning up image layer" \ && rm --force --verbose *.deb \ From 427508edf14a5fdaa3de0768f6e1a6890d48f252 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Thu, 30 Jan 2025 07:43:23 -0800 Subject: [PATCH 05/37] Feature: Enable zxing for all platforms (#8947) --- Dockerfile | 11 +++++------ Pipfile | 2 +- Pipfile.lock | 1 - docs/configuration.md | 2 -- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index c8fdcf62d..2284e9f8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -221,12 +221,11 @@ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \ && apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \ && python3 -m pip install --no-cache-dir --upgrade wheel \ && echo "Installing Python requirements" \ - && curl --fail --silent --show-error --location \ - --output psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl \ - https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl \ - && curl --fail --silent --show-error --location \ - --output psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl \ - https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl \ + && curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \ + https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl \ + https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl \ + https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_aarch64.whl \ + https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl \ && python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \ && echo "Installing NLTK data" \ && python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \ diff --git a/Pipfile b/Pipfile index 0e2486b03..1e69f316d 100644 --- a/Pipfile +++ b/Pipfile @@ -58,7 +58,7 @@ uvicorn = {extras = ["standard"], version = "==0.25.0"} watchdog = "~=6.0" whitenoise = "~=6.8" whoosh = "~=2.7" -zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"} +zxing-cpp = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index de4290d84..1889d3d4a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -2791,7 +2791,6 @@ "sha256:fbd5b253ad0f8823c5c104feaaa19acab95c217cb924b012d55ff339c42b3583", "sha256:fd3f175f7b57cfbdea56afdb5335eaebaadeebc06e20a087d9aa3f99637c4aa5" ], - "markers": "platform_machine == 'x86_64'", "version": "==2.3.0" } }, diff --git a/docs/configuration.md b/docs/configuration.md index 799620c05..359a51482 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1073,8 +1073,6 @@ or hidden folders some tools use to store data. If you have problems that your Barcodes/QR-Codes are not detected (especially with bad scan quality and/or small codes), try the other one. - zxing is not available on all platforms. - #### [`PAPERLESS_PRE_CONSUME_SCRIPT=`](#PAPERLESS_PRE_CONSUME_SCRIPT) {#PAPERLESS_PRE_CONSUME_SCRIPT} : After some initial validation, Paperless can trigger an arbitrary From f56974f158f9087cde6576ad5656d7a91c452515 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 30 Jan 2025 08:31:52 -0800 Subject: [PATCH 06/37] Feature: use 'share sheet' for download buttons on mobile (#8949) --- src-ui/messages.xlf | 237 +++++++++--------- src-ui/package-lock.json | 14 ++ src-ui/package.json | 1 + src-ui/setup-jest.ts | 9 + .../document-detail.component.html | 15 +- .../document-detail.component.spec.ts | 37 +++ .../document-detail.component.ts | 62 ++++- 7 files changed, 245 insertions(+), 130 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 981394b87..7499a56ff 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -241,18 +241,18 @@ Document was added to Paperless-ngx. src/app/app.component.ts - 93 + 95 src/app/app.component.ts - 102 + 104 Open document src/app/app.component.ts - 95 + 97 src/app/components/admin/trash/trash.component.ts @@ -279,21 +279,21 @@ Could not add : src/app/app.component.ts - 117 + 119 Document is being processed by Paperless-ngx. src/app/app.component.ts - 132 + 134 Dashboard src/app/app.component.ts - 139 + 141 src/app/components/app-frame/app-frame.component.html @@ -312,7 +312,7 @@ Documents src/app/app.component.ts - 150 + 152 src/app/components/app-frame/app-frame.component.html @@ -351,7 +351,7 @@ Settings src/app/app.component.ts - 162 + 164 src/app/components/admin/settings/settings.component.html @@ -374,74 +374,74 @@ Prev src/app/app.component.ts - 168 + 170 Next src/app/app.component.ts - 169 + 171 src/app/components/document-detail/document-detail.component.html - 95 + 100 End src/app/app.component.ts - 170 + 172 The dashboard can be used to show saved views, such as an 'Inbox'. Views are found under Manage > Saved Views once you have created some. src/app/app.component.ts - 176 + 178 Drag-and-drop documents here to start uploading or place them in the consume folder. You can also drag-and-drop documents anywhere on all other pages of the web app. Once you do, Paperless-ngx will start training its machine learning algorithms. src/app/app.component.ts - 183 + 185 The documents list shows all of your documents and allows for filtering as well as bulk-editing. There are three different view styles: list, small cards and large cards. A list of documents currently opened for editing is shown in the sidebar. src/app/app.component.ts - 188 + 190 The filtering tools allow you to quickly find documents using various searches, dates, tags, etc. src/app/app.component.ts - 195 + 197 Any combination of filters can be saved as a 'view' which can then be displayed on the dashboard and / or sidebar. src/app/app.component.ts - 201 + 203 Tags, correspondents, document types and storage paths can all be managed using these pages. They can also be created from the document edit view. src/app/app.component.ts - 206 + 208 Manage e-mail accounts and rules for automatically importing documents. src/app/app.component.ts - 214 + 216 src/app/components/manage/mail/mail.component.html @@ -452,14 +452,14 @@ Workflows give you more control over the document pipeline. src/app/app.component.ts - 222 + 224 File Tasks shows you documents that have been consumed, are waiting to be, or may have failed during the process. src/app/app.component.ts - 230 + 232 src/app/components/admin/tasks/tasks.component.html @@ -470,28 +470,28 @@ Check out the settings for various tweaks to the web app. src/app/app.component.ts - 238 + 240 Thank you! 🙏 src/app/app.component.ts - 246 + 248 There are <em>tons</em> more features and info we didn't cover here, but this should get you started. Check out the documentation or visit the project on GitHub to learn more or to report issues. src/app/app.component.ts - 248 + 250 Lastly, on behalf of every contributor to this community-supported project, thank you for using Paperless-ngx! src/app/app.component.ts - 250 + 252 @@ -534,7 +534,7 @@ src/app/components/document-detail/document-detail.component.html - 348 + 353 @@ -593,7 +593,7 @@ src/app/components/document-detail/document-detail.component.html - 341 + 346 src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html @@ -739,7 +739,7 @@ src/app/components/document-detail/document-detail.component.html - 361 + 366 src/app/components/document-list/document-list.component.html @@ -1162,7 +1162,7 @@ src/app/components/document-detail/document-detail.component.html - 317 + 322 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -1757,7 +1757,7 @@ src/app/components/document-detail/document-detail.component.html - 45 + 50 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -2232,7 +2232,7 @@ src/app/components/document-detail/document-detail.component.ts - 924 + 926 @@ -2509,19 +2509,19 @@ src/app/components/document-detail/document-detail.component.ts - 948 + 950 src/app/components/document-detail/document-detail.component.ts - 1255 + 1303 src/app/components/document-detail/document-detail.component.ts - 1294 + 1342 src/app/components/document-detail/document-detail.component.ts - 1335 + 1383 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -2954,7 +2954,7 @@ src/app/components/document-detail/document-detail.component.html - 29 + 34 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -3115,7 +3115,7 @@ src/app/components/document-detail/document-detail.component.ts - 901 + 903 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -4142,7 +4142,7 @@ src/app/components/document-detail/document-detail.component.html - 283 + 288 @@ -4974,7 +4974,7 @@ Not assigned src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts - 380 + 392 Filter drop down element to filter for documents with no correspondent/type/tag assigned @@ -4982,7 +4982,7 @@ Open filter src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts - 501 + 513 @@ -6183,14 +6183,14 @@ Download original src/app/components/document-detail/document-detail.component.html - 36 + 41 Reprocess src/app/components/document-detail/document-detail.component.html - 49 + 54 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -6201,7 +6201,7 @@ More like this src/app/components/document-detail/document-detail.component.html - 53 + 58 src/app/components/document-list/document-card-large/document-card-large.component.html @@ -6212,14 +6212,14 @@ Split src/app/components/document-detail/document-detail.component.html - 57 + 62 Rotate src/app/components/document-detail/document-detail.component.html - 61 + 66 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -6230,18 +6230,18 @@ Delete page(s) src/app/components/document-detail/document-detail.component.html - 65 + 70 Close src/app/components/document-detail/document-detail.component.html - 89 + 94 src/app/components/document-detail/document-detail.component.ts - 1312 + 1360 src/app/guards/dirty-saved-view.guard.ts @@ -6252,21 +6252,21 @@ Previous src/app/components/document-detail/document-detail.component.html - 92 + 97 Details src/app/components/document-detail/document-detail.component.html - 105 + 110 Title src/app/components/document-detail/document-detail.component.html - 108 + 113 src/app/components/document-list/document-list.component.html @@ -6289,21 +6289,21 @@ Archive serial number src/app/components/document-detail/document-detail.component.html - 109 + 114 Date created src/app/components/document-detail/document-detail.component.html - 110 + 115 Correspondent src/app/components/document-detail/document-detail.component.html - 112 + 117 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -6330,7 +6330,7 @@ Document type src/app/components/document-detail/document-detail.component.html - 114 + 119 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -6357,7 +6357,7 @@ Storage path src/app/components/document-detail/document-detail.component.html - 116 + 121 src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -6380,7 +6380,7 @@ Default src/app/components/document-detail/document-detail.component.html - 117 + 122 src/app/components/manage/saved-views/saved-views.component.html @@ -6391,14 +6391,14 @@ Content src/app/components/document-detail/document-detail.component.html - 213 + 218 Metadata src/app/components/document-detail/document-detail.component.html - 222 + 227 src/app/components/document-detail/metadata-collapse/metadata-collapse.component.ts @@ -6409,175 +6409,175 @@ Date modified src/app/components/document-detail/document-detail.component.html - 229 + 234 Date added src/app/components/document-detail/document-detail.component.html - 233 + 238 Media filename src/app/components/document-detail/document-detail.component.html - 237 + 242 Original filename src/app/components/document-detail/document-detail.component.html - 241 + 246 Original MD5 checksum src/app/components/document-detail/document-detail.component.html - 245 + 250 Original file size src/app/components/document-detail/document-detail.component.html - 249 + 254 Original mime type src/app/components/document-detail/document-detail.component.html - 253 + 258 Archive MD5 checksum src/app/components/document-detail/document-detail.component.html - 258 + 263 Archive file size src/app/components/document-detail/document-detail.component.html - 264 + 269 Original document metadata src/app/components/document-detail/document-detail.component.html - 273 + 278 Archived document metadata src/app/components/document-detail/document-detail.component.html - 276 + 281 Notes src/app/components/document-detail/document-detail.component.html - 295,298 + 300,303 History src/app/components/document-detail/document-detail.component.html - 306 + 311 Save & next src/app/components/document-detail/document-detail.component.html - 343 + 348 Save & close src/app/components/document-detail/document-detail.component.html - 346 + 351 Document loading... src/app/components/document-detail/document-detail.component.html - 356 + 361 Enter Password src/app/components/document-detail/document-detail.component.html - 410 + 415 An error occurred loading content: src/app/components/document-detail/document-detail.component.ts - 406,408 + 411,413 Document changes detected src/app/components/document-detail/document-detail.component.ts - 436 + 434 The version of this document in your browser session appears older than the existing version. src/app/components/document-detail/document-detail.component.ts - 437 + 435 Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document. src/app/components/document-detail/document-detail.component.ts - 438 + 436 Ok src/app/components/document-detail/document-detail.component.ts - 440 + 438 Next document src/app/components/document-detail/document-detail.component.ts - 547 + 545 Previous document src/app/components/document-detail/document-detail.component.ts - 557 + 555 Close document src/app/components/document-detail/document-detail.component.ts - 565 + 563 src/app/services/open-documents.service.ts @@ -6588,64 +6588,64 @@ Save document src/app/components/document-detail/document-detail.component.ts - 572 + 570 Save and close / next src/app/components/document-detail/document-detail.component.ts - 581 + 579 Error retrieving metadata src/app/components/document-detail/document-detail.component.ts - 633 + 631 Error retrieving suggestions. src/app/components/document-detail/document-detail.component.ts - 662 + 660 Document saved successfully. src/app/components/document-detail/document-detail.component.ts - 813 + 811 src/app/components/document-detail/document-detail.component.ts - 827 + 825 Error saving document src/app/components/document-detail/document-detail.component.ts - 831 + 829 src/app/components/document-detail/document-detail.component.ts - 874 + 872 Do you really want to move the document "" to the trash? src/app/components/document-detail/document-detail.component.ts - 902 + 904 Documents can be restored prior to permanent deletion. src/app/components/document-detail/document-detail.component.ts - 903 + 905 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6656,7 +6656,7 @@ Move to trash src/app/components/document-detail/document-detail.component.ts - 905 + 907 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6667,7 +6667,7 @@ Reprocess confirm src/app/components/document-detail/document-detail.component.ts - 944 + 946 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6678,70 +6678,77 @@ This operation will permanently recreate the archive file for this document. src/app/components/document-detail/document-detail.component.ts - 945 + 947 The archive file will be re-generated with the current settings. src/app/components/document-detail/document-detail.component.ts - 946 + 948 Reprocess operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content. src/app/components/document-detail/document-detail.component.ts - 956 + 958 Error executing operation src/app/components/document-detail/document-detail.component.ts - 967 + 969 + + + + Error downloading document + + src/app/components/document-detail/document-detail.component.ts + 1016 Page Fit src/app/components/document-detail/document-detail.component.ts - 1040 + 1088 Split confirm src/app/components/document-detail/document-detail.component.ts - 1253 + 1301 This operation will split the selected document(s) into new documents. src/app/components/document-detail/document-detail.component.ts - 1254 + 1302 Split operation will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1270 + 1318 Error executing split operation src/app/components/document-detail/document-detail.component.ts - 1279 + 1327 Rotate confirm src/app/components/document-detail/document-detail.component.ts - 1292 + 1340 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6752,60 +6759,60 @@ This operation will permanently rotate the original version of the current document. src/app/components/document-detail/document-detail.component.ts - 1293 + 1341 Rotation will begin in the background. Close and re-open the document after the operation has completed to see the changes. src/app/components/document-detail/document-detail.component.ts - 1309 + 1357 Error executing rotate operation src/app/components/document-detail/document-detail.component.ts - 1321 + 1369 Delete pages confirm src/app/components/document-detail/document-detail.component.ts - 1333 + 1381 This operation will permanently delete the selected pages from the original document. src/app/components/document-detail/document-detail.component.ts - 1334 + 1382 Delete pages operation will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes. src/app/components/document-detail/document-detail.component.ts - 1349 + 1397 Error executing delete pages operation src/app/components/document-detail/document-detail.component.ts - 1358 + 1406 An error occurred loading tiff: src/app/components/document-detail/document-detail.component.ts - 1398 + 1446 src/app/components/document-detail/document-detail.component.ts - 1402 + 1450 diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index c09875247..060c1afc8 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -29,6 +29,7 @@ "ngx-bootstrap-icons": "^1.9.3", "ngx-color": "^9.0.0", "ngx-cookie-service": "^19.0.0", + "ngx-device-detector": "^9.0.0", "ngx-file-drop": "^16.0.0", "ngx-ui-tour-ng-bootstrap": "^16.0.0", "rxjs": "^7.8.1", @@ -14248,6 +14249,19 @@ "@angular/core": "^19.0.0" } }, + "node_modules/ngx-device-detector": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ngx-device-detector/-/ngx-device-detector-9.0.0.tgz", + "integrity": "sha512-zpio/wqH1GnxIpWCdA7cp5fmWf7YLycgzfXzQHmB9vaS7eAcqpBF5mxDS65IhE7TzNTJziDrYJCT+9tVqBcsDg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0" + } + }, "node_modules/ngx-file-drop": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/ngx-file-drop/-/ngx-file-drop-16.0.0.tgz", diff --git a/src-ui/package.json b/src-ui/package.json index f9d4d8158..0e19f4240 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -31,6 +31,7 @@ "ngx-bootstrap-icons": "^1.9.3", "ngx-color": "^9.0.0", "ngx-cookie-service": "^19.0.0", + "ngx-device-detector": "^9.0.0", "ngx-file-drop": "^16.0.0", "ngx-ui-tour-ng-bootstrap": "^16.0.0", "rxjs": "^7.8.1", diff --git a/src-ui/setup-jest.ts b/src-ui/setup-jest.ts index 244938606..52dccaf02 100644 --- a/src-ui/setup-jest.ts +++ b/src-ui/setup-jest.ts @@ -100,6 +100,15 @@ Object.defineProperty(navigator, 'clipboard', { }, }) Object.defineProperty(navigator, 'canShare', { value: () => true }) +if (!navigator.share) { + Object.defineProperty(navigator, 'share', { value: jest.fn() }) +} +if (!URL.createObjectURL) { + Object.defineProperty(window.URL, 'createObjectURL', { value: jest.fn() }) +} +if (!URL.revokeObjectURL) { + Object.defineProperty(window.URL, 'revokeObjectURL', { value: jest.fn() }) +} Object.defineProperty(window, 'ResizeObserver', { value: mock() }) Object.defineProperty(window, 'location', { configurable: true, diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index bc0bf41fd..02fec3cf7 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -25,15 +25,20 @@
- - Download - + @if (metadata?.has_archive_version) {
- +
} diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index 0a2e5605f..8b2a84534 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -24,6 +24,7 @@ import { NgbModalRef, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { DeviceDetectorService } from 'ngx-device-detector' import { of, throwError } from 'rxjs' import { routes } from 'src/app/app-routing.module' import { Correspondent } from 'src/app/data/correspondent' @@ -127,6 +128,7 @@ describe('DocumentDetailComponent', () => { let documentListViewService: DocumentListViewService let settingsService: SettingsService let customFieldsService: CustomFieldsService + let deviceDetectorService: DeviceDetectorService let httpTestingController: HttpTestingController let componentRouterService: ComponentRouterService @@ -264,6 +266,7 @@ describe('DocumentDetailComponent', () => { settingsService = TestBed.inject(SettingsService) settingsService.currentUser = { id: 1 } customFieldsService = TestBed.inject(CustomFieldsService) + deviceDetectorService = TestBed.inject(DeviceDetectorService) fixture = TestBed.createComponent(DocumentDetailComponent) httpTestingController = TestBed.inject(HttpTestingController) componentRouterService = TestBed.inject(ComponentRouterService) @@ -1268,4 +1271,38 @@ describe('DocumentDetailComponent', () => { .error(new ErrorEvent('failed')) expect(component.tiffError).not.toBeUndefined() }) + + it('should support download using share sheet on mobile, direct download otherwise', () => { + const shareSpy = jest.spyOn(navigator, 'share') + const createSpy = jest.spyOn(document, 'createElement') + const urlRevokeSpy = jest.spyOn(URL, 'revokeObjectURL') + initNormally() + + // Mobile + jest.spyOn(deviceDetectorService, 'isDesktop').mockReturnValue(false) + component.download() + httpTestingController + .expectOne(`${environment.apiBaseUrl}documents/${doc.id}/download/`) + .error(new ProgressEvent('failed')) + expect(shareSpy).not.toHaveBeenCalled() + + component.download(true) + httpTestingController + .expectOne( + `${environment.apiBaseUrl}documents/${doc.id}/download/?original=true` + ) + .flush(new ArrayBuffer(100)) + expect(shareSpy).toHaveBeenCalled() + + // Desktop + shareSpy.mockClear() + jest.spyOn(deviceDetectorService, 'isDesktop').mockReturnValue(true) + component.download() + httpTestingController + .expectOne(`${environment.apiBaseUrl}documents/${doc.id}/download/`) + .flush(new ArrayBuffer(100)) + expect(shareSpy).not.toHaveBeenCalled() + expect(createSpy).toHaveBeenCalledWith('a') + expect(urlRevokeSpy).toHaveBeenCalled() + }) }) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 8d1b35071..0378fbb97 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -20,6 +20,7 @@ import { import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms' import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' +import { DeviceDetectorService } from 'ngx-device-detector' import { BehaviorSubject, Observable, Subject } from 'rxjs' import { debounceTime, @@ -195,8 +196,6 @@ export class DocumentDetailComponent previewUrl: string thumbUrl: string previewText: string - downloadUrl: string - downloadOriginalUrl: string previewLoaded: boolean = false tiffURL: string tiffError: string @@ -234,6 +233,9 @@ export class DocumentDetailComponent ogDate: Date customFields: CustomField[] + + public downloading: boolean = false + public readonly CustomFieldDataType = CustomFieldDataType public readonly ContentRenderType = ContentRenderType @@ -274,7 +276,8 @@ export class DocumentDetailComponent private customFieldsService: CustomFieldsService, private http: HttpClient, private hotKeyService: HotKeyService, - private componentRouterService: ComponentRouterService + private componentRouterService: ComponentRouterService, + private deviceDetectorService: DeviceDetectorService ) { super() } @@ -417,13 +420,6 @@ export class DocumentDetailComponent .pipe( switchMap((doc) => { this.documentId = doc.id - this.downloadUrl = this.documentsService.getDownloadUrl( - this.documentId - ) - this.downloadOriginalUrl = this.documentsService.getDownloadUrl( - this.documentId, - true - ) this.suggestions = null const openDocument = this.openDocumentService.getOpenDocument( this.documentId @@ -978,6 +974,52 @@ export class DocumentDetailComponent }) } + download(original: boolean = false) { + this.downloading = true + const downloadUrl = this.documentsService.getDownloadUrl( + this.documentId, + original + ) + this.http.get(downloadUrl, { responseType: 'blob' }).subscribe({ + next: (blob) => { + this.downloading = false + const blobParts = [blob] + const file = new File( + blobParts, + original + ? this.document.original_file_name + : this.document.archived_file_name, + { + type: original ? this.document.mime_type : 'application/pdf', + } + ) + if ( + !this.deviceDetectorService.isDesktop() && + navigator.canShare && + navigator.canShare({ files: [file] }) + ) { + navigator.share({ + files: [file], + }) + } else { + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = this.document.title + a.click() + URL.revokeObjectURL(url) + } + }, + error: (error) => { + this.downloading = false + this.toastService.showError( + $localize`Error downloading document`, + error + ) + }, + }) + } + hasNext() { return this.documentListViewService.hasNext(this.documentId) } From 5b8c9ef5fcbac3013aa5a563b916fe587a1a7a4c Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:55:05 -0800 Subject: [PATCH 07/37] Fix: reflect doc links in bulk modify custom fields (#8962) --- src/documents/bulk_edit.py | 91 +++++++++++++++++++++++++++ src/documents/serialisers.py | 88 +------------------------- src/documents/tests/test_bulk_edit.py | 19 ++++-- 3 files changed, 108 insertions(+), 90 deletions(-) diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index de43aed87..7e2d2088e 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -12,6 +12,7 @@ from celery import shared_task from django.conf import settings from django.contrib.auth.models import User from django.db.models import Q +from django.utils import timezone from documents.data_models import ConsumableDocument from documents.data_models import DocumentMetadataOverrides @@ -177,6 +178,12 @@ def modify_custom_fields( field_id=field_id, defaults=defaults, ) + if ( + custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK + and value + ): + doc = Document.objects.get(id=doc_id) + reflect_doclinks(doc, custom_field, value) CustomFieldInstance.objects.filter( document_id__in=affected_docs, field_id__in=remove_custom_fields, @@ -447,3 +454,87 @@ def delete_pages(doc_ids: list[int], pages: list[int]) -> Literal["OK"]: logger.exception(f"Error deleting pages from document {doc.id}: {e}") return "OK" + + +def reflect_doclinks( + document: Document, + field: CustomField, + target_doc_ids: list[int], +): + """ + Add or remove 'symmetrical' links to `document` on all `target_doc_ids` + """ + + if target_doc_ids is None: + target_doc_ids = [] + + # Check if any documents are going to be removed from the current list of links and remove the symmetrical links + current_field_instance = CustomFieldInstance.objects.filter( + field=field, + document=document, + ).first() + if current_field_instance is not None and current_field_instance.value is not None: + for doc_id in current_field_instance.value: + if doc_id not in target_doc_ids: + remove_doclink( + document=document, + field=field, + target_doc_id=doc_id, + ) + + # Create an instance if target doc doesn't have this field or append it to an existing one + existing_custom_field_instances = { + custom_field.document_id: custom_field + for custom_field in CustomFieldInstance.objects.filter( + field=field, + document_id__in=target_doc_ids, + ) + } + custom_field_instances_to_create = [] + custom_field_instances_to_update = [] + for target_doc_id in target_doc_ids: + target_doc_field_instance = existing_custom_field_instances.get( + target_doc_id, + ) + if target_doc_field_instance is None: + custom_field_instances_to_create.append( + CustomFieldInstance( + document_id=target_doc_id, + field=field, + value_document_ids=[document.id], + ), + ) + elif target_doc_field_instance.value is None: + target_doc_field_instance.value_document_ids = [document.id] + custom_field_instances_to_update.append(target_doc_field_instance) + elif document.id not in target_doc_field_instance.value: + target_doc_field_instance.value_document_ids.append(document.id) + custom_field_instances_to_update.append(target_doc_field_instance) + + CustomFieldInstance.objects.bulk_create(custom_field_instances_to_create) + CustomFieldInstance.objects.bulk_update( + custom_field_instances_to_update, + ["value_document_ids"], + ) + Document.objects.filter(id__in=target_doc_ids).update(modified=timezone.now()) + + +def remove_doclink( + document: Document, + field: CustomField, + target_doc_id: int, +): + """ + Removes a 'symmetrical' link to `document` from the target document's existing custom field instance + """ + target_doc_field_instance = CustomFieldInstance.objects.filter( + document_id=target_doc_id, + field=field, + ).first() + if ( + target_doc_field_instance is not None + and document.id in target_doc_field_instance.value + ): + target_doc_field_instance.value.remove(document.id) + target_doc_field_instance.save() + Document.objects.filter(id=target_doc_id).update(modified=timezone.now()) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 0732fd242..4adadbcb2 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -16,7 +16,6 @@ from django.core.validators import DecimalValidator from django.core.validators import MaxLengthValidator from django.core.validators import RegexValidator from django.core.validators import integer_validator -from django.utils import timezone from django.utils.crypto import get_random_string from django.utils.text import slugify from django.utils.translation import gettext as _ @@ -647,7 +646,7 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer): if custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK: # prior to update so we can look for any docs that are going to be removed - self.reflect_doclinks(document, custom_field, validated_data["value"]) + bulk_edit.reflect_doclinks(document, custom_field, validated_data["value"]) # Actually update or create the instance, providing the value # to fill in the correct attribute based on the type @@ -767,89 +766,6 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer): return ret - def reflect_doclinks( - self, - document: Document, - field: CustomField, - target_doc_ids: list[int], - ): - """ - Add or remove 'symmetrical' links to `document` on all `target_doc_ids` - """ - - if target_doc_ids is None: - target_doc_ids = [] - - # Check if any documents are going to be removed from the current list of links and remove the symmetrical links - current_field_instance = CustomFieldInstance.objects.filter( - field=field, - document=document, - ).first() - if ( - current_field_instance is not None - and current_field_instance.value is not None - ): - for doc_id in current_field_instance.value: - if doc_id not in target_doc_ids: - self.remove_doclink(document, field, doc_id) - - # Create an instance if target doc doesn't have this field or append it to an existing one - existing_custom_field_instances = { - custom_field.document_id: custom_field - for custom_field in CustomFieldInstance.objects.filter( - field=field, - document_id__in=target_doc_ids, - ) - } - custom_field_instances_to_create = [] - custom_field_instances_to_update = [] - for target_doc_id in target_doc_ids: - target_doc_field_instance = existing_custom_field_instances.get( - target_doc_id, - ) - if target_doc_field_instance is None: - custom_field_instances_to_create.append( - CustomFieldInstance( - document_id=target_doc_id, - field=field, - value_document_ids=[document.id], - ), - ) - elif target_doc_field_instance.value is None: - target_doc_field_instance.value_document_ids = [document.id] - custom_field_instances_to_update.append(target_doc_field_instance) - elif document.id not in target_doc_field_instance.value: - target_doc_field_instance.value_document_ids.append(document.id) - custom_field_instances_to_update.append(target_doc_field_instance) - - CustomFieldInstance.objects.bulk_create(custom_field_instances_to_create) - CustomFieldInstance.objects.bulk_update( - custom_field_instances_to_update, - ["value_document_ids"], - ) - Document.objects.filter(id__in=target_doc_ids).update(modified=timezone.now()) - - @staticmethod - def remove_doclink( - document: Document, - field: CustomField, - target_doc_id: int, - ): - """ - Removes a 'symmetrical' link to `document` from the target document's existing custom field instance - """ - target_doc_field_instance = CustomFieldInstance.objects.filter( - document_id=target_doc_id, - field=field, - ).first() - if ( - target_doc_field_instance is not None - and document.id in target_doc_field_instance.value - ): - target_doc_field_instance.value.remove(document.id) - target_doc_field_instance.save() - Document.objects.filter(id=target_doc_id).update(modified=timezone.now()) - class Meta: model = CustomFieldInstance fields = [ @@ -951,7 +867,7 @@ class DocumentSerializer( ): # Doc link field is being removed entirely for doc_id in custom_field_instance.value: - CustomFieldInstanceSerializer.remove_doclink( + bulk_edit.remove_doclink( instance, custom_field_instance.field, doc_id, diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index 03c177343..2d8af025b 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -268,7 +268,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) cf3 = CustomField.objects.create( name="cf3", - data_type=CustomField.FieldDataType.STRING, + data_type=CustomField.FieldDataType.DOCUMENTLINK, ) CustomFieldInstance.objects.create( document=self.doc2, @@ -282,9 +282,14 @@ class TestBulkEdit(DirectoriesMixin, TestCase): document=self.doc2, field=cf3, ) + doc3: Document = Document.objects.create( + title="doc3", + content="content", + checksum="D3", + ) bulk_edit.modify_custom_fields( [self.doc1.id, self.doc2.id], - add_custom_fields={cf2.id: None, cf3.id: "value"}, + add_custom_fields={cf2.id: None, cf3.id: [doc3.id]}, remove_custom_fields=[cf.id], ) @@ -301,7 +306,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc1.custom_fields.get(field=cf3).value, - "value", + [doc3.id], ) self.assertEqual( self.doc2.custom_fields.count(), @@ -309,7 +314,13 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc2.custom_fields.get(field=cf3).value, - "value", + [doc3.id], + ) + # assert reflect document link + doc3.refresh_from_db() + self.assertEqual( + doc3.custom_fields.first().value, + [self.doc2.id, self.doc1.id], ) self.async_task.assert_called_once() From cf7422346a09af288e349d94addcde2e24255e67 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 31 Jan 2025 00:35:12 -0800 Subject: [PATCH 08/37] Fix: resolve error in trackBy --- .../document-card-large/document-card-large.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index 6ebbd6055..84e415815 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -32,7 +32,7 @@ {{document.title | documentTitle}} } @if (displayFields.includes(DisplayField.TAGS)) { - @for (tagID of document.tags; track t) { + @for (tagID of document.tags; track tagID) { } } From 54e72d5b60a49c905265648849b59306723cccd5 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:39:22 -0800 Subject: [PATCH 09/37] Fix: also ensure symmetric doc link removal on bulk edit (#8963) --- src/documents/bulk_edit.py | 23 +++++++++++++++++---- src/documents/tests/test_bulk_edit.py | 29 ++++++++++++++++++--------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index 7e2d2088e..f0522eddc 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -178,12 +178,27 @@ def modify_custom_fields( field_id=field_id, defaults=defaults, ) - if ( - custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK - and value - ): + if custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK: doc = Document.objects.get(id=doc_id) reflect_doclinks(doc, custom_field, value) + + # For doc link fields that are being removed, remove symmetrical links + for doclink_being_removed_instance in CustomFieldInstance.objects.filter( + document_id__in=affected_docs, + field__id__in=remove_custom_fields, + field__data_type=CustomField.FieldDataType.DOCUMENTLINK, + value_document_ids__isnull=False, + ): + for target_doc_id in doclink_being_removed_instance.value: + remove_doclink( + document=Document.objects.get( + id=doclink_being_removed_instance.document.id, + ), + field=doclink_being_removed_instance.field, + target_doc_id=target_doc_id, + ) + + # Finally, remove the custom fields CustomFieldInstance.objects.filter( document_id__in=affected_docs, field_id__in=remove_custom_fields, diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index 2d8af025b..7fde5f8ee 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -282,14 +282,9 @@ class TestBulkEdit(DirectoriesMixin, TestCase): document=self.doc2, field=cf3, ) - doc3: Document = Document.objects.create( - title="doc3", - content="content", - checksum="D3", - ) bulk_edit.modify_custom_fields( [self.doc1.id, self.doc2.id], - add_custom_fields={cf2.id: None, cf3.id: [doc3.id]}, + add_custom_fields={cf2.id: None, cf3.id: [self.doc3.id]}, remove_custom_fields=[cf.id], ) @@ -306,7 +301,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc1.custom_fields.get(field=cf3).value, - [doc3.id], + [self.doc3.id], ) self.assertEqual( self.doc2.custom_fields.count(), @@ -314,12 +309,11 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc2.custom_fields.get(field=cf3).value, - [doc3.id], + [self.doc3.id], ) # assert reflect document link - doc3.refresh_from_db() self.assertEqual( - doc3.custom_fields.first().value, + self.doc3.custom_fields.first().value, [self.doc2.id, self.doc1.id], ) @@ -327,6 +321,21 @@ class TestBulkEdit(DirectoriesMixin, TestCase): args, kwargs = self.async_task.call_args self.assertCountEqual(kwargs["document_ids"], [self.doc1.id, self.doc2.id]) + # removal of document link cf, should also remove symmetric link + bulk_edit.modify_custom_fields( + [self.doc3.id], + add_custom_fields={}, + remove_custom_fields=[cf3.id], + ) + self.assertNotIn( + self.doc3.id, + self.doc1.custom_fields.filter(field=cf3).first().value, + ) + self.assertNotIn( + self.doc3.id, + self.doc2.custom_fields.filter(field=cf3).first().value, + ) + def test_delete(self): self.assertEqual(Document.objects.count(), 5) bulk_edit.delete([self.doc1.id, self.doc2.id]) From bf368aadd0da1fb1cf767977e5cbd589dad12d09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:25:21 +0000 Subject: [PATCH 10/37] Chore(deps-dev): Bump ruff from 0.9.2 to 0.9.3 in the development group (#8928) * Chore(deps-dev): Bump ruff from 0.9.2 to 0.9.3 in the development group Bumps the development group with 1 update: [ruff](https://github.com/astral-sh/ruff). Updates `ruff` from 0.9.2 to 0.9.3 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.9.2...0.9.3) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development ... Signed-off-by: dependabot[bot] * Update .pre-commit-config.yaml --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- Pipfile.lock | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 524ec771f..2ddffaf9f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,7 @@ repos: - 'prettier-plugin-organize-imports@4.1.0' # Python hooks - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.2 + rev: v0.9.3 hooks: - id: ruff - id: ruff-format diff --git a/Pipfile.lock b/Pipfile.lock index d9d05ea29..de4290d84 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -3983,28 +3983,28 @@ }, "ruff": { "hashes": [ - "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", - "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", - "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", - "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", - "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", - "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", - "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", - "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", - "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", - "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", - "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", - "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", - "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", - "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", - "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", - "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", - "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", - "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4" + "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2", + "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4", + "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439", + "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730", + "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4", + "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5", + "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624", + "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b", + "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a", + "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b", + "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5", + "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4", + "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c", + "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519", + "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1", + "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4", + "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6", + "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.9.2" + "version": "==0.9.3" }, "scipy": { "hashes": [ From c2a9ac332a5c036d997102479cbc02ff6ba9fbad Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 29 Jan 2025 07:23:44 -0800 Subject: [PATCH 11/37] Enhancement: require totp code for obtain auth token (#8936) --- src/documents/tests/test_api_permissions.py | 66 +++++++++++++++++++++ src/paperless/serialisers.py | 33 +++++++++++ src/paperless/urls.py | 4 +- src/paperless/views.py | 6 ++ 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/src/documents/tests/test_api_permissions.py b/src/documents/tests/test_api_permissions.py index 5de1887b2..3785c8f2a 100644 --- a/src/documents/tests/test_api_permissions.py +++ b/src/documents/tests/test_api_permissions.py @@ -3,6 +3,7 @@ import json from unittest import mock from allauth.mfa.models import Authenticator +from allauth.mfa.totp.internal import auth as totp_auth from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.auth.models import User @@ -488,6 +489,71 @@ class TestApiAuth(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response.data["detail"], "MFA required") + @mock.patch("allauth.mfa.totp.internal.auth.TOTP.validate_code") + def test_get_token_mfa_enabled(self, mock_validate_code): + """ + GIVEN: + - User with MFA enabled + WHEN: + - API request is made to obtain an auth token + THEN: + - MFA code is required + """ + user1 = User.objects.create_user(username="user1") + user1.set_password("password") + user1.save() + + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + }, + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + secret = totp_auth.generate_totp_secret() + totp_auth.TOTP.activate( + user1, + secret, + ) + + # no code + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + }, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data["non_field_errors"][0], "MFA code is required") + + # invalid code + mock_validate_code.return_value = False + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + "code": "123456", + }, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data["non_field_errors"][0], "Invalid MFA code") + + # valid code + mock_validate_code.return_value = True + response = self.client.post( + "/api/token/", + data={ + "username": "user1", + "password": "password", + "code": "123456", + }, + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + class TestApiUser(DirectoriesMixin, APITestCase): ENDPOINT = "/api/users/" diff --git a/src/paperless/serialisers.py b/src/paperless/serialisers.py index d5acfe465..fb1f511f7 100644 --- a/src/paperless/serialisers.py +++ b/src/paperless/serialisers.py @@ -1,11 +1,14 @@ import logging from allauth.mfa.adapter import get_adapter as get_mfa_adapter +from allauth.mfa.models import Authenticator +from allauth.mfa.totp.internal.auth import TOTP from allauth.socialaccount.models import SocialAccount from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.auth.models import User from rest_framework import serializers +from rest_framework.authtoken.serializers import AuthTokenSerializer from paperless.models import ApplicationConfiguration @@ -24,6 +27,36 @@ class ObfuscatedUserPasswordField(serializers.Field): return data +class PaperlessAuthTokenSerializer(AuthTokenSerializer): + code = serializers.CharField( + label="MFA Code", + write_only=True, + required=False, + ) + + def validate(self, attrs): + attrs = super().validate(attrs) + user = attrs.get("user") + code = attrs.get("code") + mfa_adapter = get_mfa_adapter() + if mfa_adapter.is_mfa_enabled(user): + if not code: + raise serializers.ValidationError( + "MFA code is required", + ) + authenticator = Authenticator.objects.get( + user=user, + type=Authenticator.Type.TOTP, + ) + if not TOTP(instance=authenticator).validate_code( + code, + ): + raise serializers.ValidationError( + "Invalid MFA code", + ) + return attrs + + class UserSerializer(serializers.ModelSerializer): password = ObfuscatedUserPasswordField(required=False) user_permissions = serializers.SlugRelatedField( diff --git a/src/paperless/urls.py b/src/paperless/urls.py index c528c5e2a..703a72042 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -14,7 +14,6 @@ from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic import RedirectView from django.views.static import serve -from rest_framework.authtoken import views from rest_framework.routers import DefaultRouter from documents.views import BulkDownloadView @@ -50,6 +49,7 @@ from paperless.views import DisconnectSocialAccountView from paperless.views import FaviconView from paperless.views import GenerateAuthTokenView from paperless.views import GroupViewSet +from paperless.views import PaperlessObtainAuthTokenView from paperless.views import ProfileView from paperless.views import SocialAccountProvidersView from paperless.views import TOTPView @@ -157,7 +157,7 @@ urlpatterns = [ ), path( "token/", - views.obtain_auth_token, + PaperlessObtainAuthTokenView.as_view(), ), re_path( "^profile/", diff --git a/src/paperless/views.py b/src/paperless/views.py index 03721adf2..bcabd182f 100644 --- a/src/paperless/views.py +++ b/src/paperless/views.py @@ -19,6 +19,7 @@ from django.http import HttpResponseNotFound from django.views.generic import View from django_filters.rest_framework import DjangoFilterBackend from rest_framework.authtoken.models import Token +from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.decorators import action from rest_framework.filters import OrderingFilter from rest_framework.generics import GenericAPIView @@ -35,10 +36,15 @@ from paperless.filters import UserFilterSet from paperless.models import ApplicationConfiguration from paperless.serialisers import ApplicationConfigurationSerializer from paperless.serialisers import GroupSerializer +from paperless.serialisers import PaperlessAuthTokenSerializer from paperless.serialisers import ProfileSerializer from paperless.serialisers import UserSerializer +class PaperlessObtainAuthTokenView(ObtainAuthToken): + serializer_class = PaperlessAuthTokenSerializer + + class StandardPagination(PageNumberPagination): page_size = 25 page_size_query_param = "page_size" From e0ea4a462565dad644a3563123d30bff407af889 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:55:05 -0800 Subject: [PATCH 12/37] Fix: reflect doc links in bulk modify custom fields (#8962) --- src/documents/bulk_edit.py | 91 +++++++++++++++++++++++++++ src/documents/serialisers.py | 88 +------------------------- src/documents/tests/test_bulk_edit.py | 19 ++++-- 3 files changed, 108 insertions(+), 90 deletions(-) diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index de43aed87..7e2d2088e 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -12,6 +12,7 @@ from celery import shared_task from django.conf import settings from django.contrib.auth.models import User from django.db.models import Q +from django.utils import timezone from documents.data_models import ConsumableDocument from documents.data_models import DocumentMetadataOverrides @@ -177,6 +178,12 @@ def modify_custom_fields( field_id=field_id, defaults=defaults, ) + if ( + custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK + and value + ): + doc = Document.objects.get(id=doc_id) + reflect_doclinks(doc, custom_field, value) CustomFieldInstance.objects.filter( document_id__in=affected_docs, field_id__in=remove_custom_fields, @@ -447,3 +454,87 @@ def delete_pages(doc_ids: list[int], pages: list[int]) -> Literal["OK"]: logger.exception(f"Error deleting pages from document {doc.id}: {e}") return "OK" + + +def reflect_doclinks( + document: Document, + field: CustomField, + target_doc_ids: list[int], +): + """ + Add or remove 'symmetrical' links to `document` on all `target_doc_ids` + """ + + if target_doc_ids is None: + target_doc_ids = [] + + # Check if any documents are going to be removed from the current list of links and remove the symmetrical links + current_field_instance = CustomFieldInstance.objects.filter( + field=field, + document=document, + ).first() + if current_field_instance is not None and current_field_instance.value is not None: + for doc_id in current_field_instance.value: + if doc_id not in target_doc_ids: + remove_doclink( + document=document, + field=field, + target_doc_id=doc_id, + ) + + # Create an instance if target doc doesn't have this field or append it to an existing one + existing_custom_field_instances = { + custom_field.document_id: custom_field + for custom_field in CustomFieldInstance.objects.filter( + field=field, + document_id__in=target_doc_ids, + ) + } + custom_field_instances_to_create = [] + custom_field_instances_to_update = [] + for target_doc_id in target_doc_ids: + target_doc_field_instance = existing_custom_field_instances.get( + target_doc_id, + ) + if target_doc_field_instance is None: + custom_field_instances_to_create.append( + CustomFieldInstance( + document_id=target_doc_id, + field=field, + value_document_ids=[document.id], + ), + ) + elif target_doc_field_instance.value is None: + target_doc_field_instance.value_document_ids = [document.id] + custom_field_instances_to_update.append(target_doc_field_instance) + elif document.id not in target_doc_field_instance.value: + target_doc_field_instance.value_document_ids.append(document.id) + custom_field_instances_to_update.append(target_doc_field_instance) + + CustomFieldInstance.objects.bulk_create(custom_field_instances_to_create) + CustomFieldInstance.objects.bulk_update( + custom_field_instances_to_update, + ["value_document_ids"], + ) + Document.objects.filter(id__in=target_doc_ids).update(modified=timezone.now()) + + +def remove_doclink( + document: Document, + field: CustomField, + target_doc_id: int, +): + """ + Removes a 'symmetrical' link to `document` from the target document's existing custom field instance + """ + target_doc_field_instance = CustomFieldInstance.objects.filter( + document_id=target_doc_id, + field=field, + ).first() + if ( + target_doc_field_instance is not None + and document.id in target_doc_field_instance.value + ): + target_doc_field_instance.value.remove(document.id) + target_doc_field_instance.save() + Document.objects.filter(id=target_doc_id).update(modified=timezone.now()) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 0732fd242..4adadbcb2 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -16,7 +16,6 @@ from django.core.validators import DecimalValidator from django.core.validators import MaxLengthValidator from django.core.validators import RegexValidator from django.core.validators import integer_validator -from django.utils import timezone from django.utils.crypto import get_random_string from django.utils.text import slugify from django.utils.translation import gettext as _ @@ -647,7 +646,7 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer): if custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK: # prior to update so we can look for any docs that are going to be removed - self.reflect_doclinks(document, custom_field, validated_data["value"]) + bulk_edit.reflect_doclinks(document, custom_field, validated_data["value"]) # Actually update or create the instance, providing the value # to fill in the correct attribute based on the type @@ -767,89 +766,6 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer): return ret - def reflect_doclinks( - self, - document: Document, - field: CustomField, - target_doc_ids: list[int], - ): - """ - Add or remove 'symmetrical' links to `document` on all `target_doc_ids` - """ - - if target_doc_ids is None: - target_doc_ids = [] - - # Check if any documents are going to be removed from the current list of links and remove the symmetrical links - current_field_instance = CustomFieldInstance.objects.filter( - field=field, - document=document, - ).first() - if ( - current_field_instance is not None - and current_field_instance.value is not None - ): - for doc_id in current_field_instance.value: - if doc_id not in target_doc_ids: - self.remove_doclink(document, field, doc_id) - - # Create an instance if target doc doesn't have this field or append it to an existing one - existing_custom_field_instances = { - custom_field.document_id: custom_field - for custom_field in CustomFieldInstance.objects.filter( - field=field, - document_id__in=target_doc_ids, - ) - } - custom_field_instances_to_create = [] - custom_field_instances_to_update = [] - for target_doc_id in target_doc_ids: - target_doc_field_instance = existing_custom_field_instances.get( - target_doc_id, - ) - if target_doc_field_instance is None: - custom_field_instances_to_create.append( - CustomFieldInstance( - document_id=target_doc_id, - field=field, - value_document_ids=[document.id], - ), - ) - elif target_doc_field_instance.value is None: - target_doc_field_instance.value_document_ids = [document.id] - custom_field_instances_to_update.append(target_doc_field_instance) - elif document.id not in target_doc_field_instance.value: - target_doc_field_instance.value_document_ids.append(document.id) - custom_field_instances_to_update.append(target_doc_field_instance) - - CustomFieldInstance.objects.bulk_create(custom_field_instances_to_create) - CustomFieldInstance.objects.bulk_update( - custom_field_instances_to_update, - ["value_document_ids"], - ) - Document.objects.filter(id__in=target_doc_ids).update(modified=timezone.now()) - - @staticmethod - def remove_doclink( - document: Document, - field: CustomField, - target_doc_id: int, - ): - """ - Removes a 'symmetrical' link to `document` from the target document's existing custom field instance - """ - target_doc_field_instance = CustomFieldInstance.objects.filter( - document_id=target_doc_id, - field=field, - ).first() - if ( - target_doc_field_instance is not None - and document.id in target_doc_field_instance.value - ): - target_doc_field_instance.value.remove(document.id) - target_doc_field_instance.save() - Document.objects.filter(id=target_doc_id).update(modified=timezone.now()) - class Meta: model = CustomFieldInstance fields = [ @@ -951,7 +867,7 @@ class DocumentSerializer( ): # Doc link field is being removed entirely for doc_id in custom_field_instance.value: - CustomFieldInstanceSerializer.remove_doclink( + bulk_edit.remove_doclink( instance, custom_field_instance.field, doc_id, diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index 03c177343..2d8af025b 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -268,7 +268,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) cf3 = CustomField.objects.create( name="cf3", - data_type=CustomField.FieldDataType.STRING, + data_type=CustomField.FieldDataType.DOCUMENTLINK, ) CustomFieldInstance.objects.create( document=self.doc2, @@ -282,9 +282,14 @@ class TestBulkEdit(DirectoriesMixin, TestCase): document=self.doc2, field=cf3, ) + doc3: Document = Document.objects.create( + title="doc3", + content="content", + checksum="D3", + ) bulk_edit.modify_custom_fields( [self.doc1.id, self.doc2.id], - add_custom_fields={cf2.id: None, cf3.id: "value"}, + add_custom_fields={cf2.id: None, cf3.id: [doc3.id]}, remove_custom_fields=[cf.id], ) @@ -301,7 +306,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc1.custom_fields.get(field=cf3).value, - "value", + [doc3.id], ) self.assertEqual( self.doc2.custom_fields.count(), @@ -309,7 +314,13 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc2.custom_fields.get(field=cf3).value, - "value", + [doc3.id], + ) + # assert reflect document link + doc3.refresh_from_db() + self.assertEqual( + doc3.custom_fields.first().value, + [self.doc2.id, self.doc1.id], ) self.async_task.assert_called_once() From ee72e2d1fdd1b3e2149ca4097af1c3f86eeb42ea Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 31 Jan 2025 00:35:12 -0800 Subject: [PATCH 13/37] Fix: resolve error in trackBy --- .../document-card-large/document-card-large.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index 6ebbd6055..84e415815 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -32,7 +32,7 @@ {{document.title | documentTitle}} } @if (displayFields.includes(DisplayField.TAGS)) { - @for (tagID of document.tags; track t) { + @for (tagID of document.tags; track tagID) { } } From e1d8680698b91f335b1130773f10179cfda829bc Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:39:22 -0800 Subject: [PATCH 14/37] Fix: also ensure symmetric doc link removal on bulk edit (#8963) --- src/documents/bulk_edit.py | 23 +++++++++++++++++---- src/documents/tests/test_bulk_edit.py | 29 ++++++++++++++++++--------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index 7e2d2088e..f0522eddc 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -178,12 +178,27 @@ def modify_custom_fields( field_id=field_id, defaults=defaults, ) - if ( - custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK - and value - ): + if custom_field.data_type == CustomField.FieldDataType.DOCUMENTLINK: doc = Document.objects.get(id=doc_id) reflect_doclinks(doc, custom_field, value) + + # For doc link fields that are being removed, remove symmetrical links + for doclink_being_removed_instance in CustomFieldInstance.objects.filter( + document_id__in=affected_docs, + field__id__in=remove_custom_fields, + field__data_type=CustomField.FieldDataType.DOCUMENTLINK, + value_document_ids__isnull=False, + ): + for target_doc_id in doclink_being_removed_instance.value: + remove_doclink( + document=Document.objects.get( + id=doclink_being_removed_instance.document.id, + ), + field=doclink_being_removed_instance.field, + target_doc_id=target_doc_id, + ) + + # Finally, remove the custom fields CustomFieldInstance.objects.filter( document_id__in=affected_docs, field_id__in=remove_custom_fields, diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index 2d8af025b..7fde5f8ee 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -282,14 +282,9 @@ class TestBulkEdit(DirectoriesMixin, TestCase): document=self.doc2, field=cf3, ) - doc3: Document = Document.objects.create( - title="doc3", - content="content", - checksum="D3", - ) bulk_edit.modify_custom_fields( [self.doc1.id, self.doc2.id], - add_custom_fields={cf2.id: None, cf3.id: [doc3.id]}, + add_custom_fields={cf2.id: None, cf3.id: [self.doc3.id]}, remove_custom_fields=[cf.id], ) @@ -306,7 +301,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc1.custom_fields.get(field=cf3).value, - [doc3.id], + [self.doc3.id], ) self.assertEqual( self.doc2.custom_fields.count(), @@ -314,12 +309,11 @@ class TestBulkEdit(DirectoriesMixin, TestCase): ) self.assertEqual( self.doc2.custom_fields.get(field=cf3).value, - [doc3.id], + [self.doc3.id], ) # assert reflect document link - doc3.refresh_from_db() self.assertEqual( - doc3.custom_fields.first().value, + self.doc3.custom_fields.first().value, [self.doc2.id, self.doc1.id], ) @@ -327,6 +321,21 @@ class TestBulkEdit(DirectoriesMixin, TestCase): args, kwargs = self.async_task.call_args self.assertCountEqual(kwargs["document_ids"], [self.doc1.id, self.doc2.id]) + # removal of document link cf, should also remove symmetric link + bulk_edit.modify_custom_fields( + [self.doc3.id], + add_custom_fields={}, + remove_custom_fields=[cf3.id], + ) + self.assertNotIn( + self.doc3.id, + self.doc1.custom_fields.filter(field=cf3).first().value, + ) + self.assertNotIn( + self.doc3.id, + self.doc2.custom_fields.filter(field=cf3).first().value, + ) + def test_delete(self): self.assertEqual(Document.objects.count(), 5) bulk_edit.delete([self.doc1.id, self.doc2.id]) From 49b658a9447e82326c4dfc9ec4983495533b1ea4 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:46:43 -0800 Subject: [PATCH 15/37] Bump version to 2.14.7 --- src-ui/src/environments/environment.prod.ts | 2 +- src/paperless/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index 0fbbdae0a..9db14f6c3 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -5,7 +5,7 @@ export const environment = { apiBaseUrl: document.baseURI + 'api/', apiVersion: '7', appTitle: 'Paperless-ngx', - version: '2.14.6', + version: '2.14.7', webSocketHost: window.location.host, webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:', webSocketBaseUrl: base_url.pathname + 'ws/', diff --git a/src/paperless/version.py b/src/paperless/version.py index 185208da1..b09a20ef4 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1,6 +1,6 @@ from typing import Final -__version__: Final[tuple[int, int, int]] = (2, 14, 6) +__version__: Final[tuple[int, int, int]] = (2, 14, 7) # Version string like X.Y.Z __full_version_str__: Final[str] = ".".join(map(str, __version__)) # Version string like X.Y From 16d6bb7334f8144a0ea62db954ec8ecf7cdff817 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 08:36:29 -0800 Subject: [PATCH 16/37] Documentation: Add v2.14.7 changelog (#8972) * Changelog v2.14.7 - GHA * Update changelog.md --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/changelog.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e2d2b98ac..56316942c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,28 @@ # Changelog +## paperless-ngx 2.14.7 + +### Features + +- Enhancement: require totp code for obtain auth token by [@shamoon](https://github.com/shamoon) [#8936](https://github.com/paperless-ngx/paperless-ngx/pull/8936) + +### Bug Fixes + +- Enhancement: require totp code for obtain auth token by [@shamoon](https://github.com/shamoon) [#8936](https://github.com/paperless-ngx/paperless-ngx/pull/8936) +- Fix: reflect doc links in bulk modify custom fields by [@shamoon](https://github.com/shamoon) [#8962](https://github.com/paperless-ngx/paperless-ngx/pull/8962) +- Fix: also ensure symmetric doc link removal on bulk edit by [@shamoon](https://github.com/shamoon) [#8963](https://github.com/paperless-ngx/paperless-ngx/pull/8963) + +### All App Changes + +
+4 changes + +- Chore(deps-dev): Bump ruff from 0.9.2 to 0.9.3 in the development group by @[dependabot[bot]](https://github.com/apps/dependabot) [#8928](https://github.com/paperless-ngx/paperless-ngx/pull/8928) +- Enhancement: require totp code for obtain auth token by [@shamoon](https://github.com/shamoon) [#8936](https://github.com/paperless-ngx/paperless-ngx/pull/8936) +- Fix: reflect doc links in bulk modify custom fields by [@shamoon](https://github.com/shamoon) [#8962](https://github.com/paperless-ngx/paperless-ngx/pull/8962) +- Fix: also ensure symmetric doc link removal on bulk edit by [@shamoon](https://github.com/shamoon) [#8963](https://github.com/paperless-ngx/paperless-ngx/pull/8963) +
+ ## paperless-ngx 2.14.6 ### Bug Fixes From befb80bddfcccaab48f4c0fb0b3b54d6ceaa7ba6 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 1 Feb 2025 00:18:11 -0800 Subject: [PATCH 17/37] Fix: Dont include frame component in component router service --- src-ui/src/app/services/component-router.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src-ui/src/app/services/component-router.service.ts b/src-ui/src/app/services/component-router.service.ts index 3f97584b7..0589ef61f 100644 --- a/src-ui/src/app/services/component-router.service.ts +++ b/src-ui/src/app/services/component-router.service.ts @@ -2,6 +2,8 @@ import { Injectable } from '@angular/core' import { ActivationStart, Event, Router } from '@angular/router' import { filter } from 'rxjs' +const EXCLUDE_COMPONENTS = ['AppFrameComponent'] + @Injectable({ providedIn: 'root', }) @@ -15,7 +17,8 @@ export class ComponentRouterService { .subscribe((event: ActivationStart) => { if ( this.componentHistory[this.componentHistory.length - 1] !== - event.snapshot.component.name + event.snapshot.component.name && + !EXCLUDE_COMPONENTS.includes(event.snapshot.component.name) ) { this.history.push(event.snapshot.url.toString()) this.componentHistory.push(event.snapshot.component.name) From 8597911d8527f012d791146129ef646583d90dea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:51:46 -0800 Subject: [PATCH 18/37] Chore(deps): Bump the frontend-angular-dependencies group (#8986) Bumps the frontend-angular-dependencies group in /src-ui with 23 updates: | Package | From | To | | --- | --- | --- | | [@angular/cdk](https://github.com/angular/components) | `19.0.2` | `19.1.2` | | [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `19.0.3` | `19.1.4` | | [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `19.0.3` | `19.1.4` | | [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `19.0.3` | `19.1.4` | | [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `19.0.3` | `19.1.4` | | [@angular/localize](https://github.com/angular/angular) | `19.0.3` | `19.1.4` | | [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `19.0.3` | `19.1.4` | | [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `19.0.3` | `19.1.4` | | [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `19.0.3` | `19.1.4` | | [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `14.1.0` | `14.2.0` | | [ngx-cookie-service](https://github.com/stevermeister/ngx-cookie-service) | `19.0.0` | `19.1.0` | | [@angular-builders/custom-webpack](https://github.com/just-jeb/angular-builders/tree/HEAD/packages/custom-webpack) | `19.0.0-beta.0` | `19.0.0` | | [@angular-builders/jest](https://github.com/just-jeb/angular-builders/tree/HEAD/packages/jest) | `19.0.0-beta.1` | `19.0.0` | | [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `19.0.4` | `19.1.5` | | [@angular-devkit/core](https://github.com/angular/angular-cli) | `19.0.4` | `19.1.5` | | [@angular-devkit/schematics](https://github.com/angular/angular-cli) | `19.0.4` | `19.1.5` | | [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `19.0.0` | `19.0.2` | | [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `19.0.0` | `19.0.2` | | [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `19.0.0` | `19.0.2` | | [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `19.0.0` | `19.0.2` | | [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `19.0.0` | `19.0.2` | | [@angular/cli](https://github.com/angular/angular-cli) | `19.0.4` | `19.1.5` | | [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `19.0.3` | `19.1.4` | Updates `@angular/cdk` from 19.0.2 to 19.1.2 - [Release notes](https://github.com/angular/components/releases) - [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/components/compare/19.0.2...19.1.2) Updates `@angular/common` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/common) Updates `@angular/compiler` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/compiler) Updates `@angular/core` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/core) Updates `@angular/forms` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/forms) Updates `@angular/localize` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/compare/19.0.3...19.1.4) Updates `@angular/platform-browser` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/platform-browser) Updates `@angular/platform-browser-dynamic` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/platform-browser-dynamic) Updates `@angular/router` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/router) Updates `@ng-select/ng-select` from 14.1.0 to 14.2.0 - [Release notes](https://github.com/ng-select/ng-select/releases) - [Changelog](https://github.com/ng-select/ng-select/blob/master/CHANGELOG.md) - [Commits](https://github.com/ng-select/ng-select/compare/v14.1.0...v14.2.0) Updates `ngx-cookie-service` from 19.0.0 to 19.1.0 - [Release notes](https://github.com/stevermeister/ngx-cookie-service/releases) - [Changelog](https://github.com/stevermeister/ngx-cookie-service/blob/master/CHANGELOG.md) - [Commits](https://github.com/stevermeister/ngx-cookie-service/compare/v19.0.0...19.1.0) Updates `@angular-builders/custom-webpack` from 19.0.0-beta.0 to 19.0.0 - [Release notes](https://github.com/just-jeb/angular-builders/releases) - [Changelog](https://github.com/just-jeb/angular-builders/blob/master/packages/custom-webpack/CHANGELOG.md) - [Commits](https://github.com/just-jeb/angular-builders/commits/@angular-builders/custom-webpack@19.0.0/packages/custom-webpack) Updates `@angular-builders/jest` from 19.0.0-beta.1 to 19.0.0 - [Release notes](https://github.com/just-jeb/angular-builders/releases) - [Changelog](https://github.com/just-jeb/angular-builders/blob/master/packages/jest/CHANGELOG.md) - [Commits](https://github.com/just-jeb/angular-builders/commits/@angular-builders/jest@19.0.0/packages/jest) Updates `@angular-devkit/build-angular` from 19.0.4 to 19.1.5 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/19.0.4...19.1.5) Updates `@angular-devkit/core` from 19.0.4 to 19.1.5 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/19.0.4...19.1.5) Updates `@angular-devkit/schematics` from 19.0.4 to 19.1.5 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/19.0.4...19.1.5) Updates `@angular-eslint/builder` from 19.0.0 to 19.0.2 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/builder/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v19.0.2/packages/builder) Updates `@angular-eslint/eslint-plugin` from 19.0.0 to 19.0.2 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v19.0.2/packages/eslint-plugin) Updates `@angular-eslint/eslint-plugin-template` from 19.0.0 to 19.0.2 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v19.0.2/packages/eslint-plugin-template) Updates `@angular-eslint/schematics` from 19.0.0 to 19.0.2 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/schematics/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v19.0.2/packages/schematics) Updates `@angular-eslint/template-parser` from 19.0.0 to 19.0.2 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/template-parser/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v19.0.2/packages/template-parser) Updates `@angular/cli` from 19.0.4 to 19.1.5 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/19.0.4...19.1.5) Updates `@angular/compiler-cli` from 19.0.3 to 19.1.4 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/19.1.4/packages/compiler-cli) --- updated-dependencies: - dependency-name: "@angular/cdk" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/common" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/compiler" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/core" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/forms" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/localize" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/platform-browser" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/platform-browser-dynamic" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/router" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@ng-select/ng-select" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: ngx-cookie-service dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-builders/custom-webpack" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular-builders/jest" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular-devkit/build-angular" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-devkit/core" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-devkit/schematics" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/builder" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/eslint-plugin-template" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/schematics" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/template-parser" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular/cli" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/compiler-cli" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 3288 ++++++++++++++++++-------------------- src-ui/package.json | 44 +- 2 files changed, 1595 insertions(+), 1737 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 060c1afc8..9ad8cf38e 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -9,17 +9,17 @@ "version": "0.0.0", "hasInstallScript": true, "dependencies": { - "@angular/cdk": "^19.0.2", - "@angular/common": "~19.0.3", - "@angular/compiler": "~19.0.3", - "@angular/core": "~19.0.3", - "@angular/forms": "~19.0.3", - "@angular/localize": "~19.0.3", - "@angular/platform-browser": "~19.0.3", - "@angular/platform-browser-dynamic": "~19.0.3", - "@angular/router": "~19.0.3", + "@angular/cdk": "^19.1.2", + "@angular/common": "~19.1.4", + "@angular/compiler": "~19.1.4", + "@angular/core": "~19.1.4", + "@angular/forms": "~19.1.4", + "@angular/localize": "~19.1.4", + "@angular/platform-browser": "~19.1.4", + "@angular/platform-browser-dynamic": "~19.1.4", + "@angular/router": "~19.1.4", "@ng-bootstrap/ng-bootstrap": "^18.0.0", - "@ng-select/ng-select": "^14.1.0", + "@ng-select/ng-select": "^14.2.0", "@ngneat/dirty-check-forms": "^3.0.3", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.3", @@ -28,7 +28,7 @@ "ng2-pdf-viewer": "^10.4.0", "ngx-bootstrap-icons": "^1.9.3", "ngx-color": "^9.0.0", - "ngx-cookie-service": "^19.0.0", + "ngx-cookie-service": "^19.1.0", "ngx-device-detector": "^9.0.0", "ngx-file-drop": "^16.0.0", "ngx-ui-tour-ng-bootstrap": "^16.0.0", @@ -39,18 +39,18 @@ "zone.js": "^0.15.0" }, "devDependencies": { - "@angular-builders/custom-webpack": "^19.0.0-beta.0", - "@angular-builders/jest": "^19.0.0-beta.1", + "@angular-builders/custom-webpack": "^19.0.0", + "@angular-builders/jest": "^19.0.0", "@angular-devkit/build-angular": "^19.0.4", - "@angular-devkit/core": "^19.0.4", - "@angular-devkit/schematics": "^19.0.4", - "@angular-eslint/builder": "19.0.0", - "@angular-eslint/eslint-plugin": "19.0.0", - "@angular-eslint/eslint-plugin-template": "19.0.0", - "@angular-eslint/schematics": "19.0.0", - "@angular-eslint/template-parser": "19.0.0", - "@angular/cli": "~19.0.4", - "@angular/compiler-cli": "~19.0.3", + "@angular-devkit/core": "^19.1.5", + "@angular-devkit/schematics": "^19.1.5", + "@angular-eslint/builder": "19.0.2", + "@angular-eslint/eslint-plugin": "19.0.2", + "@angular-eslint/eslint-plugin-template": "19.0.2", + "@angular-eslint/schematics": "19.0.2", + "@angular-eslint/template-parser": "19.0.2", + "@angular/cli": "~19.1.5", + "@angular/compiler-cli": "~19.1.4", "@codecov/webpack-plugin": "^1.2.1", "@playwright/test": "^1.48.2", "@types/jest": "^29.5.14", @@ -138,9 +138,9 @@ } }, "node_modules/@angular-builders/common": { - "version": "3.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@angular-builders/common/-/common-3.0.0-beta.0.tgz", - "integrity": "sha512-3OUBr4UMUoyZJkDxne3HWAKLMLYZWf3nTQ/34ICErvkfBnV9NPsYs47nGuKechHWqhz7MVk2JeKztZw3hXWobA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@angular-builders/common/-/common-3.0.0.tgz", + "integrity": "sha512-AACGMwlBFYF3PaFekgJDCmqO1hMBrK5eyjHMN5aqJk3PV46BhnlNcQEa9pftLUKxoGijXBQzlalDZkceatyoMw==", "dev": true, "license": "MIT", "dependencies": { @@ -153,13 +153,13 @@ } }, "node_modules/@angular-builders/custom-webpack": { - "version": "19.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-19.0.0-beta.0.tgz", - "integrity": "sha512-FdpYku8Q9rh6L04FU+yMZqWGKVY2OD9k5I0veIztYLoRFc9VxEPp7gmoO/9GmXfCL90zv89BPJw/QZ7sonQ5YA==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-19.0.0.tgz", + "integrity": "sha512-MD3N+OPl/50u+N6YJ6UwS1kOT5C6RkOsSih8F88lR/TACQCSDf0FLmJmROktNgRNADhySGnK18o874Vftyqi4w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-builders/common": "3.0.0-beta.0", + "@angular-builders/common": "3.0.0", "@angular-devkit/architect": ">=0.1900.0 < 0.2000.0", "@angular-devkit/build-angular": "^19.0.0", "@angular-devkit/core": "^19.0.0", @@ -174,13 +174,13 @@ } }, "node_modules/@angular-builders/jest": { - "version": "19.0.0-beta.1", - "resolved": "https://registry.npmjs.org/@angular-builders/jest/-/jest-19.0.0-beta.1.tgz", - "integrity": "sha512-s6NeJGHf09F/kArhnxj+DypEQ1nuloA5RwmkTjCCCGAsXnS8WdJiyizaTmWj1WiSnYkY2431HqjLfwvUmRo3qQ==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@angular-builders/jest/-/jest-19.0.0.tgz", + "integrity": "sha512-DEKragHT26kwUhXx9goYehQ/WxFzpVrMHIicYF+L7sLVmFyCwPwAslYZZBe/eTm/x++tKRHnun1lbcf1ZDccRg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-builders/common": "3.0.0-beta.0", + "@angular-builders/common": "3.0.0", "@angular-devkit/architect": ">=0.1900.0 < 0.2000.0", "@angular-devkit/core": "^19.0.0", "jest-preset-angular": "14.4.2", @@ -197,14 +197,42 @@ "jest": ">=29" } }, - "node_modules/@angular-devkit/architect": { - "version": "0.1900.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1900.4.tgz", - "integrity": "sha512-9XwZ21BPYS2vGOOwVB40fsMyuwJT0H1lWaAMo8Umwi6XbKBVfaWbEhjtR9dlarrySKtFuTz9hmTZkIXHLjXPdA==", + "node_modules/@angular-builders/jest/node_modules/jest-preset-angular": { + "version": "14.4.2", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.4.2.tgz", + "integrity": "sha512-BYYv0FaTDfBNh8WyA9mpOV3krfw20kurBGK8INZUnv7KZDAWZuQtCET4TwTWxSNQ9jS1OX1+a5weCm/bTDDM1A==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.0.4", + "bs-logger": "^0.2.6", + "esbuild-wasm": ">=0.15.13", + "jest-environment-jsdom": "^29.0.0", + "jest-util": "^29.0.0", + "pretty-format": "^29.0.0", + "ts-jest": "^29.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": ">=0.15.13" + }, + "peerDependencies": { + "@angular/compiler-cli": ">=15.0.0 <20.0.0", + "@angular/core": ">=15.0.0 <20.0.0", + "@angular/platform-browser-dynamic": ">=15.0.0 <20.0.0", + "jest": "^29.0.0", + "typescript": ">=4.8" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1901.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1901.5.tgz", + "integrity": "sha512-zlRudZx34FkFZnSdaQCjxDleHwbQYNLdBFcLi+FBwt0UXqxmhbEIasK3l/3kCOC3QledrjUzVXgouji+OZ/WGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.1.5", "rxjs": "7.8.1" }, "engines": { @@ -214,19 +242,19 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.0.4.tgz", - "integrity": "sha512-n7fcRdNB7ed5j6aZI+qPI/1LylFv1OiRNgBIeJxX3HEmzQxsHHLcxWog2yZK2Fvw3390xFx/VjZaklITj6tBFA==", + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.1.5.tgz", + "integrity": "sha512-ny7ktNOTxaEi6cS3V6XFP5bbJkgiMt3OUNUYLdfdbv4y6wolVlPVHKl+wb4xs6tgbnmx63+e6zGpoDMCRytgcg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1900.4", - "@angular-devkit/build-webpack": "0.1900.4", - "@angular-devkit/core": "19.0.4", - "@angular/build": "19.0.4", + "@angular-devkit/architect": "0.1901.5", + "@angular-devkit/build-webpack": "0.1901.5", + "@angular-devkit/core": "19.1.5", + "@angular/build": "19.1.5", "@babel/core": "7.26.0", - "@babel/generator": "7.26.2", + "@babel/generator": "7.26.3", "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", "@babel/plugin-transform-async-generator-functions": "7.25.9", @@ -235,21 +263,21 @@ "@babel/preset-env": "7.26.0", "@babel/runtime": "7.26.0", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "19.0.4", - "@vitejs/plugin-basic-ssl": "1.1.0", + "@ngtools/webpack": "19.1.5", + "@vitejs/plugin-basic-ssl": "1.2.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", "babel-loader": "9.2.1", "browserslist": "^4.21.5", "copy-webpack-plugin": "12.0.2", "css-loader": "7.1.2", - "esbuild-wasm": "0.24.0", - "fast-glob": "3.3.2", + "esbuild-wasm": "0.24.2", + "fast-glob": "3.3.3", "http-proxy-middleware": "3.0.3", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", - "less": "4.2.0", + "less": "4.2.1", "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.3.1", @@ -257,22 +285,22 @@ "open": "10.1.0", "ora": "5.4.1", "picomatch": "4.0.2", - "piscina": "4.7.0", + "piscina": "4.8.0", "postcss": "8.4.49", "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.80.7", - "sass-loader": "16.0.3", + "sass": "1.83.1", + "sass-loader": "16.0.4", "semver": "7.6.3", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.36.0", + "terser": "5.37.0", "tree-kill": "1.2.2", "tslib": "2.8.1", - "webpack": "5.96.1", + "webpack": "5.97.1", "webpack-dev-middleware": "7.4.2", - "webpack-dev-server": "5.1.0", + "webpack-dev-server": "5.2.0", "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" }, @@ -282,14 +310,14 @@ "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.24.0" + "esbuild": "0.24.2" }, "peerDependencies": { "@angular/compiler-cli": "^19.0.0", "@angular/localize": "^19.0.0", "@angular/platform-server": "^19.0.0", "@angular/service-worker": "^19.0.0", - "@angular/ssr": "^19.0.4", + "@angular/ssr": "^19.1.5", "@web/test-runner": "^0.19.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", @@ -297,8 +325,8 @@ "karma": "^6.3.0", "ng-packagr": "^19.0.0", "protractor": "^7.0.0", - "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.5 <5.7" + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "typescript": ">=5.5 <5.8" }, "peerDependenciesMeta": { "@angular/localize": { @@ -339,27 +367,92 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/aix-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", - "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", - "cpu": [ - "ppc64" - ], + "node_modules/@angular-devkit/build-angular/node_modules/@angular/build": { + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.1.5.tgz", + "integrity": "sha512-byoHcv0/s6WGWap59s43N/eC+4NsviuTnGoj+iR0ayubk8snn6jdkZLbFDfnTuQlTiu4ok8/XcksjzeMkgGyyw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1901.5", + "@angular-devkit/core": "19.1.5", + "@babel/core": "7.26.0", + "@babel/helper-annotate-as-pure": "7.25.9", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-syntax-import-attributes": "7.26.0", + "@inquirer/confirm": "5.1.1", + "@vitejs/plugin-basic-ssl": "1.2.0", + "beasties": "0.2.0", + "browserslist": "^4.23.0", + "esbuild": "0.24.2", + "fast-glob": "3.3.3", + "https-proxy-agent": "7.0.6", + "istanbul-lib-instrument": "6.0.3", + "listr2": "8.2.5", + "magic-string": "0.30.17", + "mrmime": "2.0.0", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.8.0", + "rollup": "4.30.1", + "sass": "1.83.1", + "semver": "7.6.3", + "vite": "6.0.11", + "watchpack": "2.4.2" + }, "engines": { - "node": ">=18" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "lmdb": "3.2.2" + }, + "peerDependencies": { + "@angular/compiler": "^19.0.0", + "@angular/compiler-cli": "^19.0.0", + "@angular/localize": "^19.0.0", + "@angular/platform-server": "^19.0.0", + "@angular/service-worker": "^19.0.0", + "@angular/ssr": "^19.1.5", + "less": "^4.2.0", + "ng-packagr": "^19.0.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "typescript": ">=5.5 <5.8" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@angular/ssr": { + "optional": true + }, + "less": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", - "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz", + "integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==", "cpu": [ "arm" ], @@ -368,15 +461,12 @@ "optional": true, "os": [ "android" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", - "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-android-arm64": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz", + "integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==", "cpu": [ "arm64" ], @@ -385,32 +475,12 @@ "optional": true, "os": [ "android" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", - "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/darwin-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", - "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz", + "integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==", "cpu": [ "arm64" ], @@ -419,15 +489,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/darwin-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", - "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-darwin-x64": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz", + "integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==", "cpu": [ "x64" ], @@ -436,15 +503,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", - "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz", + "integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==", "cpu": [ "arm64" ], @@ -453,15 +517,12 @@ "optional": true, "os": [ "freebsd" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/freebsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", - "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz", + "integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==", "cpu": [ "x64" ], @@ -470,15 +531,12 @@ "optional": true, "os": [ "freebsd" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", - "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz", + "integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==", "cpu": [ "arm" ], @@ -487,15 +545,26 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", - "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz", + "integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz", + "integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==", "cpu": [ "arm64" ], @@ -504,32 +573,26 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", - "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz", + "integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==", "cpu": [ - "ia32" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-loong64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", - "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz", + "integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==", "cpu": [ "loong64" ], @@ -538,32 +601,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-mips64el": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", - "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", - "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz", + "integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==", "cpu": [ "ppc64" ], @@ -572,15 +615,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-riscv64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", - "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz", + "integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==", "cpu": [ "riscv64" ], @@ -589,15 +629,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-s390x": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", - "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz", + "integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==", "cpu": [ "s390x" ], @@ -606,15 +643,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", - "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz", + "integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==", "cpu": [ "x64" ], @@ -623,15 +657,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/netbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", - "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz", + "integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==", "cpu": [ "x64" ], @@ -639,50 +670,13 @@ "license": "MIT", "optional": true, "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } + "linux" + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/openbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", - "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/sunos-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", - "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", - "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz", + "integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==", "cpu": [ "arm64" ], @@ -691,15 +685,12 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", - "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz", + "integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==", "cpu": [ "ia32" ], @@ -708,15 +699,12 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">=18" - } + ] }, - "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", - "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "node_modules/@angular-devkit/build-angular/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz", + "integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==", "cpu": [ "x64" ], @@ -725,57 +713,14 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/esbuild": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", - "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.0", - "@esbuild/android-arm": "0.24.0", - "@esbuild/android-arm64": "0.24.0", - "@esbuild/android-x64": "0.24.0", - "@esbuild/darwin-arm64": "0.24.0", - "@esbuild/darwin-x64": "0.24.0", - "@esbuild/freebsd-arm64": "0.24.0", - "@esbuild/freebsd-x64": "0.24.0", - "@esbuild/linux-arm": "0.24.0", - "@esbuild/linux-arm64": "0.24.0", - "@esbuild/linux-ia32": "0.24.0", - "@esbuild/linux-loong64": "0.24.0", - "@esbuild/linux-mips64el": "0.24.0", - "@esbuild/linux-ppc64": "0.24.0", - "@esbuild/linux-riscv64": "0.24.0", - "@esbuild/linux-s390x": "0.24.0", - "@esbuild/linux-x64": "0.24.0", - "@esbuild/netbsd-x64": "0.24.0", - "@esbuild/openbsd-arm64": "0.24.0", - "@esbuild/openbsd-x64": "0.24.0", - "@esbuild/sunos-x64": "0.24.0", - "@esbuild/win32-arm64": "0.24.0", - "@esbuild/win32-ia32": "0.24.0", - "@esbuild/win32-x64": "0.24.0" - } + ] }, "node_modules/@angular-devkit/build-angular/node_modules/istanbul-lib-instrument": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -787,14 +732,53 @@ "node": ">=10" } }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1900.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1900.4.tgz", - "integrity": "sha512-eovr5Am8EwxF7d/y0Hbfz/KYWnOXXVXVwquPUcg8JBI19lLbfctz4+71Vjz2qGroijr2FlZztRpmhd498SLt/A==", + "node_modules/@angular-devkit/build-angular/node_modules/rollup": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz", + "integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1900.4", + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.30.1", + "@rollup/rollup-android-arm64": "4.30.1", + "@rollup/rollup-darwin-arm64": "4.30.1", + "@rollup/rollup-darwin-x64": "4.30.1", + "@rollup/rollup-freebsd-arm64": "4.30.1", + "@rollup/rollup-freebsd-x64": "4.30.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.30.1", + "@rollup/rollup-linux-arm-musleabihf": "4.30.1", + "@rollup/rollup-linux-arm64-gnu": "4.30.1", + "@rollup/rollup-linux-arm64-musl": "4.30.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.30.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1", + "@rollup/rollup-linux-riscv64-gnu": "4.30.1", + "@rollup/rollup-linux-s390x-gnu": "4.30.1", + "@rollup/rollup-linux-x64-gnu": "4.30.1", + "@rollup/rollup-linux-x64-musl": "4.30.1", + "@rollup/rollup-win32-arm64-msvc": "4.30.1", + "@rollup/rollup-win32-ia32-msvc": "4.30.1", + "@rollup/rollup-win32-x64-msvc": "4.30.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1901.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1901.5.tgz", + "integrity": "sha512-UxEoF7F8L1GpH/N4me7VGe5ZPfxIiVHyhw5/ck3rcVbT6YD22/GYFGSJRGYP+D7LLTJ7OOQvfD6Bc/q62HhWvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1901.5", "rxjs": "7.8.1" }, "engines": { @@ -808,9 +792,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.0.4.tgz", - "integrity": "sha512-+imxIj1JLr2hbUYQePHgkTUKr0VmlxNSZvIREcCWtXUcdCypiwhJAtGXv6MfpB4hAx+FJZYEpVWeLwYOS/gW0A==", + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.1.5.tgz", + "integrity": "sha512-wGKV+i5mCM/Hd/3CsdrIYcVi5G2Wg/D5941bUDXivrbsqHfKVINxAkI3OI1eaD90VnAL8ICrQEoAhh6ni2Umkg==", "dev": true, "license": "MIT", "dependencies": { @@ -853,15 +837,15 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.0.4.tgz", - "integrity": "sha512-2r6Qs4N5NSPho+qzegCYS8kIgylXyH4DHaS7HJ5+4XvM1I8V8AII8payLWkUK0i29XufVoD5XfPUFnjxZrBfYQ==", + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.1.5.tgz", + "integrity": "sha512-8QjOlO2CktcTT0TWcaABea2xSePxoPKaZu96+6gc8oZzj/y8DbdGiO9mRvIac9+m4hiZI41Cqm1W+yMsCzYMkA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.0.4", + "@angular-devkit/core": "19.1.5", "jsonc-parser": "3.3.1", - "magic-string": "0.30.12", + "magic-string": "0.30.17", "ora": "5.4.1", "rxjs": "7.8.1" }, @@ -872,9 +856,9 @@ } }, "node_modules/@angular-eslint/builder": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.0.0.tgz", - "integrity": "sha512-vi68ADoEKrg2SB87jwUCaVhOhWPpXyG6X8QJzg8AiYDCQY721x1l6Pdz6WZOPruWALyoIyFGFXqtuysDGqIBhw==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.0.2.tgz", + "integrity": "sha512-BdmMSndQt2fSBiTVniskUcUpQaeweUapbsL0IDfQ7a13vL0NVXpc3K89YXuVE/xsb08uHtqphuwxPAAj6kX3OA==", "dev": true, "license": "MIT", "dependencies": { @@ -887,21 +871,21 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.0.0.tgz", - "integrity": "sha512-q6IaiqKYcmBW/gw55tytDucguo5E48szVCLNLHUFdN98YDDsP+KM3MPWYPyZcXpusmFfIjLdr8d41PlKmyMUpg==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.0.2.tgz", + "integrity": "sha512-HPmp92r70SNO/0NdIaIhxrgVSpomqryuUk7jszvNRtu+OzYCJGcbLhQD38T3dbBWT/AV0QXzyzExn6/2ai9fEw==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.0.0.tgz", - "integrity": "sha512-WkUnH5zmvC/cH6f8BGiRK+KebrKdGbQmhtu3IHLEyzG9U4mBiIV8XkSzhdkY3RCN8bKqhmE5C3oNBLNCtvg4QQ==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.0.2.tgz", + "integrity": "sha512-DLuNVVGGFicSThOcMSJyNje+FZSPdG0B3lCBRiqcgKH/16kfM4pV8MobPM7RGK2NhaOmmZ4zzJNwpwWPSgi+Lw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.0.0", - "@angular-eslint/utils": "19.0.0" + "@angular-eslint/bundled-angular-compiler": "19.0.2", + "@angular-eslint/utils": "19.0.2" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -910,14 +894,14 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.0.0.tgz", - "integrity": "sha512-d2NzuAyvFo00QGBv6BLno0KZ3Ptd+UNVHpI9vwU0giaZcjVsdKbcMvMfynkvHAAwVIVw5aSLwabIjnm0rc3x3A==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.0.2.tgz", + "integrity": "sha512-f/OCF9ThnxQ8m0eNYPwnCrySQPhYfCOF6STL7F9LnS8Bs3ZeW3/oT1yLaMIZ1Eg0ogIkgxksMAJZjrJPUPBD1Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.0.0", - "@angular-eslint/utils": "19.0.0", + "@angular-eslint/bundled-angular-compiler": "19.0.2", + "@angular-eslint/utils": "19.0.2", "aria-query": "5.3.2", "axobject-query": "4.1.0" }, @@ -928,17 +912,47 @@ "typescript": "*" } }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@angular-eslint/utils": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.0.2.tgz", + "integrity": "sha512-HotBT8OKr7zCaX1S9k27JuhRiTVIbbYVl6whlb3uwdMIPIWY8iOcEh1tjI4qDPUafpLfR72Dhwi5bO1E17F3/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "19.0.2" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@angular-eslint/utils": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.0.2.tgz", + "integrity": "sha512-HotBT8OKr7zCaX1S9k27JuhRiTVIbbYVl6whlb3uwdMIPIWY8iOcEh1tjI4qDPUafpLfR72Dhwi5bO1E17F3/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "19.0.2" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, "node_modules/@angular-eslint/schematics": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.0.0.tgz", - "integrity": "sha512-fle4SMxjI+91y5eR6hVG7yhzJHAw87LudHw918hGUVn2INIAW1TTuuQNoah8kNg9I6ICIDat26IenD4nOau6Gg==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.0.2.tgz", + "integrity": "sha512-wI4SyiAnUCrpigtK6PHRlVWMC9vWljqmlLhbsJV5O5yDajlmRdvgXvSHDefhJm0hSfvZYRXuiAARYv2+QVfnGA==", "dev": true, "license": "MIT", "dependencies": { "@angular-devkit/core": ">= 19.0.0 < 20.0.0", "@angular-devkit/schematics": ">= 19.0.0 < 20.0.0", - "@angular-eslint/eslint-plugin": "19.0.0", - "@angular-eslint/eslint-plugin-template": "19.0.0", + "@angular-eslint/eslint-plugin": "19.0.2", + "@angular-eslint/eslint-plugin-template": "19.0.2", "ignore": "6.0.2", "semver": "7.6.3", "strip-json-comments": "3.1.1" @@ -955,13 +969,13 @@ } }, "node_modules/@angular-eslint/template-parser": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.0.0.tgz", - "integrity": "sha512-bOLMNBQbrLMujGWSda0SF8ka7snQ9Uzxie1dr5LquI104p2J4Wt90DOoaWzhNaBBwedt3WXmhSHmvvR9720kHA==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.0.2.tgz", + "integrity": "sha512-z3rZd2sBfuYcFf9rGDsB2zz2fbGX8kkF+0ftg9eocyQmzWrlZHFmuw9ha7oP/Mz8gpblyCS/aa1U/Srs6gz0UQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.0.0", + "@angular-eslint/bundled-angular-compiler": "19.0.2", "eslint-scope": "^8.0.2" }, "peerDependencies": { @@ -969,550 +983,10 @@ "typescript": "*" } }, - "node_modules/@angular-eslint/utils": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.0.0.tgz", - "integrity": "sha512-PH40BmIcIr5ldr08XYnqJ8cTzJfScJjBym4SECsilBnz5fhCdTD7UEQiW4d0P78Ie8H5PxvOJx9ZE+L4WBNrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.0.0" - }, - "peerDependencies": { - "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": "*" - } - }, - "node_modules/@angular/build": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.0.4.tgz", - "integrity": "sha512-ubsNjLb54VkZwcPQ21Ke8aAHiIrRIcv7gG3R6/6XOoWeK1K2+tsv8bnO4mz5cHgzWOspLOT7FDC83NJjrKX3Nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1900.4", - "@babel/core": "7.26.0", - "@babel/helper-annotate-as-pure": "7.25.9", - "@babel/helper-split-export-declaration": "7.24.7", - "@babel/plugin-syntax-import-attributes": "7.26.0", - "@inquirer/confirm": "5.0.2", - "@vitejs/plugin-basic-ssl": "1.1.0", - "beasties": "0.1.0", - "browserslist": "^4.23.0", - "esbuild": "0.24.0", - "fast-glob": "3.3.2", - "https-proxy-agent": "7.0.5", - "istanbul-lib-instrument": "6.0.3", - "listr2": "8.2.5", - "magic-string": "0.30.12", - "mrmime": "2.0.0", - "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "4.0.2", - "piscina": "4.7.0", - "rollup": "4.26.0", - "sass": "1.80.7", - "semver": "7.6.3", - "vite": "5.4.11", - "watchpack": "2.4.2" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "optionalDependencies": { - "lmdb": "3.1.5" - }, - "peerDependencies": { - "@angular/compiler": "^19.0.0", - "@angular/compiler-cli": "^19.0.0", - "@angular/localize": "^19.0.0", - "@angular/platform-server": "^19.0.0", - "@angular/service-worker": "^19.0.0", - "@angular/ssr": "^19.0.4", - "less": "^4.2.0", - "postcss": "^8.4.0", - "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.5 <5.7" - }, - "peerDependenciesMeta": { - "@angular/localize": { - "optional": true - }, - "@angular/platform-server": { - "optional": true - }, - "@angular/service-worker": { - "optional": true - }, - "@angular/ssr": { - "optional": true - }, - "less": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tailwindcss": { - "optional": true - } - } - }, - "node_modules/@angular/build/node_modules/@esbuild/aix-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", - "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/android-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", - "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/android-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", - "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/android-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", - "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/darwin-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", - "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/darwin-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", - "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", - "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/freebsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", - "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", - "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", - "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", - "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-loong64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", - "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-mips64el": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", - "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", - "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-riscv64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", - "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-s390x": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", - "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/linux-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", - "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/netbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", - "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/openbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", - "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/sunos-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", - "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/win32-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", - "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/win32-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", - "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/@esbuild/win32-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", - "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular/build/node_modules/esbuild": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", - "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.0", - "@esbuild/android-arm": "0.24.0", - "@esbuild/android-arm64": "0.24.0", - "@esbuild/android-x64": "0.24.0", - "@esbuild/darwin-arm64": "0.24.0", - "@esbuild/darwin-x64": "0.24.0", - "@esbuild/freebsd-arm64": "0.24.0", - "@esbuild/freebsd-x64": "0.24.0", - "@esbuild/linux-arm": "0.24.0", - "@esbuild/linux-arm64": "0.24.0", - "@esbuild/linux-ia32": "0.24.0", - "@esbuild/linux-loong64": "0.24.0", - "@esbuild/linux-mips64el": "0.24.0", - "@esbuild/linux-ppc64": "0.24.0", - "@esbuild/linux-riscv64": "0.24.0", - "@esbuild/linux-s390x": "0.24.0", - "@esbuild/linux-x64": "0.24.0", - "@esbuild/netbsd-x64": "0.24.0", - "@esbuild/openbsd-arm64": "0.24.0", - "@esbuild/openbsd-x64": "0.24.0", - "@esbuild/sunos-x64": "0.24.0", - "@esbuild/win32-arm64": "0.24.0", - "@esbuild/win32-ia32": "0.24.0", - "@esbuild/win32-x64": "0.24.0" - } - }, - "node_modules/@angular/build/node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@angular/cdk": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.0.2.tgz", - "integrity": "sha512-eDjHJJWpgnzC3pR6N0gCdh51Q1ffoh6mql06YSqprj005aNKBjmCMnpU4bPPzdGSkKsjwAZWGUNWg4RS+R+iZQ==", + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.1.2.tgz", + "integrity": "sha512-rzrZ4BkGNIZWSdw0OsuSB/H9UB5ppPvmBq+uRHdYmZoYjo5wu1pmePxAIZDIBR8xdaNy9rZ4ecS6IebDkgYPrg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1527,26 +1001,26 @@ } }, "node_modules/@angular/cli": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.0.4.tgz", - "integrity": "sha512-jxnD9qkhelcRMCrHDCxNsWgn6HQCvMIj8uI0T2eB9Vy93q2YWUo/fWl2Sy4gFlR+VNeF+1hYhPLb/vqLLzjWuA==", + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.1.5.tgz", + "integrity": "sha512-bedjH3jUcrLgN3GOTTuvjbPcY3Lm0YcYBVY35S1ugI88UK6nbtttiRdgK++Qk2Q8wbg6zuaBAr4ACbfPMsnRaA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1900.4", - "@angular-devkit/core": "19.0.4", - "@angular-devkit/schematics": "19.0.4", - "@inquirer/prompts": "7.1.0", + "@angular-devkit/architect": "0.1901.5", + "@angular-devkit/core": "19.1.5", + "@angular-devkit/schematics": "19.1.5", + "@inquirer/prompts": "7.2.1", "@listr2/prompt-adapter-inquirer": "2.0.18", - "@schematics/angular": "19.0.4", + "@schematics/angular": "19.1.5", "@yarnpkg/lockfile": "1.1.0", "ini": "5.0.0", "jsonc-parser": "3.3.1", "listr2": "8.2.5", - "npm-package-arg": "12.0.0", + "npm-package-arg": "12.0.1", "npm-pick-manifest": "10.0.0", "pacote": "20.0.0", - "resolve": "1.22.8", + "resolve": "1.22.10", "semver": "7.6.3", "symbol-observable": "4.0.0", "yargs": "17.7.2" @@ -1561,9 +1035,9 @@ } }, "node_modules/@angular/common": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.0.3.tgz", - "integrity": "sha512-YyBVZU+LQ38R+/U5vF/b1T3muROKpR0kkupMw7VKnGhQfgrRX5Dk3H2nr9ritt0zPc7TOUuQSlHMf3QWah2GDg==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.1.4.tgz", + "integrity": "sha512-E4MCl13VIotOxmzKQ/UGciPeaRXQgH7ymesEjYVGcT8jmC+qz5dEcoN7L5Jvq9aUsmLBt9MFp/B5QqKCIXMqYA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1572,14 +1046,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "19.0.3", + "@angular/core": "19.1.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.0.3.tgz", - "integrity": "sha512-cxtK4SlHAPstcXfjwOaoR1dAszrzo2iDF8ZiihbZPgKUG3m27qIU3Lp5XBgxfZPlO4jh6TXkWznY7f6Tyxkb0Q==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.1.4.tgz", + "integrity": "sha512-9vGUZ+QhGWvf5dfeILybrh5rvZQtNqS8WumMeX2/vCb0JTA0N4DsL1Sy47HuWcgKBxbmHVUdF5/iufcFaqk2FA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1588,7 +1062,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "19.0.3" + "@angular/core": "19.1.4" }, "peerDependenciesMeta": { "@angular/core": { @@ -1597,9 +1071,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.0.3.tgz", - "integrity": "sha512-nayLcC3hSHoGKXCZInMdFcIZJEHYkEGNsdAutgCMuSj+lXCGuRUysuGC0rGzJc2R6nhgfaLJnO8T/O5acqaqdA==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.1.4.tgz", + "integrity": "sha512-ozJvTUzPOgFqlz69YnV14Ncod+iH0cXZvUKerjw8o+JsixLG2LmJpwQ79Gh4a/ZQmAkAxMAYYK5izCiio8MmTg==", "license": "MIT", "dependencies": { "@babel/core": "7.26.0", @@ -1620,14 +1094,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "19.0.3", - "typescript": ">=5.5 <5.7" + "@angular/compiler": "19.1.4", + "typescript": ">=5.5 <5.8" } }, "node_modules/@angular/core": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.0.3.tgz", - "integrity": "sha512-WM844gDzrbHtcM2TJB9DmfCmenUYyNSI6h924CeppDW5oG8ShinQGiWNjF5oI6EZ4tG60uK3QvCm3kjr1dmbOA==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.1.4.tgz", + "integrity": "sha512-r3T81lM9evmuW36HA3VAxIJ61M8kirGR8yHoln9fXSnYG8UeJ7JlWEbVRHmVHKOB48VK0bS/VxqN+w9TOq3bZg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1641,9 +1115,9 @@ } }, "node_modules/@angular/forms": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.0.3.tgz", - "integrity": "sha512-8wf8yDR6cW+lOhpzhmxUOiI5Wjr1Kf7o8NuJ2P5K6b7IMNRzRyR5q/6R4NUwtF6aaJ1wNqmSof+goQmtn1HOcw==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.1.4.tgz", + "integrity": "sha512-dcf4G+vXrfvy5NAP+C4A2rBeaZuwKs/TeWjZDpkRUPQMwTvDJcSNH+pqOeVsYUGNY2BkY1uPjzmgZh4F5NMQ9A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1652,21 +1126,21 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "19.0.3", - "@angular/core": "19.0.3", - "@angular/platform-browser": "19.0.3", + "@angular/common": "19.1.4", + "@angular/core": "19.1.4", + "@angular/platform-browser": "19.1.4", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/localize": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-19.0.3.tgz", - "integrity": "sha512-xou8bCPpIn0h6GJm6isiV0qkzi7C/fnF5fC4ueiN/Bp6fOuRNdwTSwaTWz4RoWvgwbQs5eZ6yIKUb+9toUAOPw==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-19.1.4.tgz", + "integrity": "sha512-AFfaxnGUWl1QZGmhYNTH8adWynSqjNwHweOUQ/ItIQ+MkbIPOpAtZp+ar6SRJZpatR59O8797jPKVFTAebLvLQ==", "license": "MIT", "dependencies": { "@babel/core": "7.26.0", "@types/babel__core": "7.20.5", - "fast-glob": "3.3.2", + "fast-glob": "3.3.3", "yargs": "^17.2.1" }, "bin": { @@ -1678,14 +1152,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "19.0.3", - "@angular/compiler-cli": "19.0.3" + "@angular/compiler": "19.1.4", + "@angular/compiler-cli": "19.1.4" } }, "node_modules/@angular/platform-browser": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.0.3.tgz", - "integrity": "sha512-vggWHSzOsCpYqnGq5IIN+n7xdEvXfgUGaMdgzPhFMTsnlMTUs5+VEFl9tX9FANHkXKB5S1RttVyvEXRqJM9ncQ==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.1.4.tgz", + "integrity": "sha512-IoVIvemj7ni6GLDCvwtZhTgMQjPyG+xPW7rASN2RVl9T3uS1fJUpXrh5JzBcCikIj20O2KV9mqt7p4iIXy9jbQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1694,9 +1168,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "19.0.3", - "@angular/common": "19.0.3", - "@angular/core": "19.0.3" + "@angular/animations": "19.1.4", + "@angular/common": "19.1.4", + "@angular/core": "19.1.4" }, "peerDependenciesMeta": { "@angular/animations": { @@ -1705,9 +1179,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.0.3.tgz", - "integrity": "sha512-gFh+QN7JvepnD3mS0XmOtDmfY8h5sSkk2/guesE2A68Na8q+M3fGZlz7I37tCXToLth5us1X0Gi0UPCSESc4SA==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.1.4.tgz", + "integrity": "sha512-r1AM8qkjl63cg46tgOHsVV4URHDctcVpt98DU/d/yN8JAugrx6GA1qOM/HMDspMjEIU4aYcSkUUY6h6uIkYmOQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1716,16 +1190,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "19.0.3", - "@angular/compiler": "19.0.3", - "@angular/core": "19.0.3", - "@angular/platform-browser": "19.0.3" + "@angular/common": "19.1.4", + "@angular/compiler": "19.1.4", + "@angular/core": "19.1.4", + "@angular/platform-browser": "19.1.4" } }, "node_modules/@angular/router": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.0.3.tgz", - "integrity": "sha512-L/s8crRC6nj5knmHsnPeOXMNdC7vUOSOvTQonXhmT0FdlP9bPnnRrNeVDnLnd8AzjPSBfIFE2eQw6T8jCwdxMA==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.1.4.tgz", + "integrity": "sha512-0gEhGGqcCS7adKuv/XeQjRbhEqRXPhIH4ygjwfonV+uvmK+C1sf+bnAt4o01hxwf12w4FcnNPkgBKt+rJJ+LpA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1734,9 +1208,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "19.0.3", - "@angular/core": "19.0.3", - "@angular/platform-browser": "19.0.3", + "@angular/common": "19.1.4", + "@angular/core": "19.1.4", + "@angular/platform-browser": "19.1.4", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -1805,13 +1279,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -1989,10 +1463,11 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -2016,15 +1491,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2113,11 +1588,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -2502,13 +1978,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2922,13 +2398,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -3235,13 +2711,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -3453,15 +2929,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -3469,10 +2946,27 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -3561,297 +3055,332 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", "cpu": [ - "x64" + "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", - "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", "cpu": [ "arm64" ], @@ -3866,83 +3395,88 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -4168,15 +3702,15 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.2.tgz", - "integrity": "sha512-+gznPl8ip8P8HYHYecDtUtdsh1t2jvb+sWCD72GAiZ9m45RqwrLmReDaqdC0umQfamtFXVRoMVJ2/qINKGm9Tg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.7.tgz", + "integrity": "sha512-lyoF4uYdBBTnqeB1gjPdYkiQ++fz/iYKaP9DON1ZGlldkvAEJsjaOBRdbl5UW1pOSslBRd701jxhAG0MlhHd2w==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/figures": "^1.0.8", - "@inquirer/type": "^3.0.1", + "@inquirer/core": "^10.1.5", + "@inquirer/figures": "^1.0.10", + "@inquirer/type": "^3.0.3", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -4188,14 +3722,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.2.tgz", - "integrity": "sha512-KJLUHOaKnNCYzwVbryj3TNBxyZIrr56fR5N45v6K9IPrbT6B7DcudBMfylkV1A8PUdJE15mybkEQyp2/ZUpxUA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.1.tgz", + "integrity": "sha512-vVLSbGci+IKQvDOtzpPTCOiEJCNidHcAq9JYVoWTW0svb5FiwSLotkM+JXNXejfjnzVYV9n0DTBythl9+XgTxg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/type": "^3.0.1" + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2" }, "engines": { "node": ">=18" @@ -4205,19 +3739,18 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.0.tgz", - "integrity": "sha512-I+ETk2AL+yAVbvuKx5AJpQmoaWhpiTFOg/UJb7ZkMAK4blmtG8ATh5ct+T/8xNld0CZG/2UhtkdMwpgvld92XQ==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.5.tgz", + "integrity": "sha512-/vyCWhET0ktav/mUeBqJRYTwmjFPIKPRYb3COAw7qORULgipGSUO2vL32lQKki3UxDKJ8BvuEbokaoyCA6YlWw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.8", - "@inquirer/type": "^3.0.1", + "@inquirer/figures": "^1.0.10", + "@inquirer/type": "^3.0.3", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, @@ -4239,14 +3772,14 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.1.0.tgz", - "integrity": "sha512-K1gGWsxEqO23tVdp5MT3H799OZ4ER1za7Dlc8F4um0W7lwSv0KGR/YyrUEyimj0g7dXZd8XknM/5QA2/Uy+TbA==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.4.tgz", + "integrity": "sha512-S8b6+K9PLzxiFGGc02m4syhEu5JsH0BukzRsuZ+tpjJ5aDsDX1WfNfOil2fmsO36Y1RMcpJGxlfQ1yh4WfU28Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/type": "^3.0.1", + "@inquirer/core": "^10.1.5", + "@inquirer/type": "^3.0.3", "external-editor": "^3.1.0" }, "engines": { @@ -4257,14 +3790,14 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.2.tgz", - "integrity": "sha512-WdgCX1cUtinz+syKyZdJomovULYlKUWZbVYZzhf+ZeeYf4htAQ3jLymoNs3koIAKfZZl3HUBb819ClCBfyznaw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.7.tgz", + "integrity": "sha512-PsUQ5t7r+DPjW0VVEHzssOTBM2UPHnvBNse7hzuki7f6ekRL94drjjfBLrGEDe7cgj3pguufy/cuFwMeWUWHXw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/type": "^3.0.1", + "@inquirer/core": "^10.1.5", + "@inquirer/type": "^3.0.3", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -4275,9 +3808,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.8.tgz", - "integrity": "sha512-tKd+jsmhq21AP1LhexC0pPwsCxEhGgAkg28byjJAd+xhmIs8LUX8JbUc3vBf3PhLxWiB5EvyBE5X7JSPAqMAqg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.10.tgz", + "integrity": "sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==", "dev": true, "license": "MIT", "engines": { @@ -4285,14 +3818,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.0.2.tgz", - "integrity": "sha512-yCLCraigU085EcdpIVEDgyfGv4vBiE4I+k1qRkc9C5dMjWF42ADMGy1RFU94+eZlz4YlkmFsiyHZy0W1wdhaNg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.4.tgz", + "integrity": "sha512-CKKF8otRBdIaVnRxkFLs00VNA9HWlEh3x4SqUfC3A8819TeOZpTYG/p+4Nqu3hh97G+A0lxkOZNYE7KISgU8BA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/type": "^3.0.1" + "@inquirer/core": "^10.1.5", + "@inquirer/type": "^3.0.3" }, "engines": { "node": ">=18" @@ -4302,14 +3835,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.2.tgz", - "integrity": "sha512-MKQhYofdUNk7eqJtz52KvM1dH6R93OMrqHduXCvuefKrsiMjHiMwjc3NZw5Imm2nqY7gWd9xdhYrtcHMJQZUxA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.7.tgz", + "integrity": "sha512-uU2nmXGC0kD8+BLgwZqcgBD1jcw2XFww2GmtP6b4504DkOp+fFAhydt7JzRR1TAI2dmj175p4SZB0lxVssNreA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/type": "^3.0.1" + "@inquirer/core": "^10.1.5", + "@inquirer/type": "^3.0.3" }, "engines": { "node": ">=18" @@ -4319,14 +3852,14 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.2.tgz", - "integrity": "sha512-tQXGSu7IO07gsYlGy3VgXRVsbOWqFBMbqAUrJSc1PDTQQ5Qdm+QVwkP0OC0jnUZ62D19iPgXOMO+tnWG+HhjNQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.7.tgz", + "integrity": "sha512-DFpqWLx+C5GV5zeFWuxwDYaeYnTWYphO07pQ2VnP403RIqRIpwBG0ATWf7pF+3IDbaXEtWatCJWxyDrJ+rkj2A==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/type": "^3.0.1", + "@inquirer/core": "^10.1.5", + "@inquirer/type": "^3.0.3", "ansi-escapes": "^4.3.2" }, "engines": { @@ -4337,22 +3870,22 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.1.0.tgz", - "integrity": "sha512-5U/XiVRH2pp1X6gpNAjWOglMf38/Ys522ncEHIKT1voRUvSj/DQnR22OVxHnwu5S+rCFaUiPQ57JOtMFQayqYA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.2.1.tgz", + "integrity": "sha512-v2JSGri6/HXSfoGIwuKEn8sNCQK6nsB2BNpy2lSX6QH9bsECrMv93QHnj5+f+1ZWpF/VNioIV2B/PDox8EvGuQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.0.2", - "@inquirer/confirm": "^5.0.2", - "@inquirer/editor": "^4.1.0", - "@inquirer/expand": "^4.0.2", - "@inquirer/input": "^4.0.2", - "@inquirer/number": "^3.0.2", - "@inquirer/password": "^4.0.2", - "@inquirer/rawlist": "^4.0.2", - "@inquirer/search": "^3.0.2", - "@inquirer/select": "^4.0.2" + "@inquirer/checkbox": "^4.0.4", + "@inquirer/confirm": "^5.1.1", + "@inquirer/editor": "^4.2.1", + "@inquirer/expand": "^4.0.4", + "@inquirer/input": "^4.1.1", + "@inquirer/number": "^3.0.4", + "@inquirer/password": "^4.0.4", + "@inquirer/rawlist": "^4.0.4", + "@inquirer/search": "^3.0.4", + "@inquirer/select": "^4.0.4" }, "engines": { "node": ">=18" @@ -4362,14 +3895,14 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.2.tgz", - "integrity": "sha512-3XGcskMoVF8H0Dl1S5TSZ3rMPPBWXRcM0VeNVsS4ByWeWjSeb0lPqfnBg6N7T0608I1B2bSVnbi2cwCrmOD1Yw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.7.tgz", + "integrity": "sha512-ZeBca+JCCtEIwQMvhuROT6rgFQWWvAImdQmIIP3XoyDFjrp2E0gZlEn65sWIoR6pP2EatYK96pvx0887OATWQQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/type": "^3.0.1", + "@inquirer/core": "^10.1.5", + "@inquirer/type": "^3.0.3", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -4380,15 +3913,15 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.2.tgz", - "integrity": "sha512-Zv4FC7w4dJ13BOJfKRQCICQfShinGjb1bCEIHxTSnjj2telu3+3RHwHubPG9HyD4aix5s+lyAMEK/wSFD75HLA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.7.tgz", + "integrity": "sha512-Krq925SDoLh9AWSNee8mbSIysgyWtcPnSAp5YtPBGCQ+OCO+5KGC8FwLpyxl8wZ2YAov/8Tp21stTRK/fw5SGg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/figures": "^1.0.8", - "@inquirer/type": "^3.0.1", + "@inquirer/core": "^10.1.5", + "@inquirer/figures": "^1.0.10", + "@inquirer/type": "^3.0.3", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -4399,15 +3932,15 @@ } }, "node_modules/@inquirer/select": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.2.tgz", - "integrity": "sha512-uSWUzaSYAEj0hlzxa1mUB6VqrKaYx0QxGBLZzU4xWFxaSyGaXxsSE4OSOwdU24j0xl8OajgayqFXW0l2bkl2kg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.7.tgz", + "integrity": "sha512-ejGBMDSD+Iqk60u5t0Zf2UQhGlJWDM78Ep70XpNufIfc+f4VOTeybYKXu9pDjz87FkRzLiVsGpQG2SzuGlhaJw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/figures": "^1.0.8", - "@inquirer/type": "^3.0.1", + "@inquirer/core": "^10.1.5", + "@inquirer/figures": "^1.0.10", + "@inquirer/type": "^3.0.3", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -4419,9 +3952,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.1.tgz", - "integrity": "sha512-+ksJMIy92sOAiAccGpcKZUc3bYO07cADnscIxHBknEm3uNts3movSmBofc1908BNy5edKscxYeAdaX1NXkHS6A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.3.tgz", + "integrity": "sha512-I4VIHFxUuY1bshGbXZTxCmhwaaEst9s/lll3ekok+o1Z26/ZUKdx8y1b7lsoG6rtsBDwEGfiBJ2SfirjoISLpg==", "dev": true, "license": "MIT", "engines": { @@ -4943,9 +4476,9 @@ } }, "node_modules/@jsonjoy.com/json-pack": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", - "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.1.tgz", + "integrity": "sha512-osjeBqMJ2lb/j/M8NCPjs1ylqWIcTRTycIhVB5pt6LgzgeRSb0YRZ7j9RfA8wIUrsr/medIuhVyonXRZWLyfdw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5029,9 +4562,9 @@ } }, "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.1.5.tgz", - "integrity": "sha512-ue5PSOzHMCIYrfvPP/MRS6hsKKLzqqhcdAvJCO8uFlDdj598EhgnacuOTuqA6uBK5rgiZXfDWyb7DVZSiBKxBA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.2.2.tgz", + "integrity": "sha512-WBSJT9Z7DTol5viq+DZD2TapeWOw7mlwXxiSBHgAzqVwsaVb0h/ekMD9iu/jDD8MUA20tO9N0WEdnT06fsUp+g==", "cpu": [ "arm64" ], @@ -5043,9 +4576,9 @@ ] }, "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.1.5.tgz", - "integrity": "sha512-CGhsb0R5vE6mMNCoSfxHFD8QTvBHM51gs4DBeigTYHWnYv2V5YpJkC4rMo5qAAFifuUcc0+a8a3SIU0c9NrfNw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.2.2.tgz", + "integrity": "sha512-4S13kUtR7c/j/MzkTIBJCXv52hQ41LG2ukeaqw4Eng9K0pNKLFjo1sDSz96/yKhwykxrWDb13ddJ/ZqD3rAhUA==", "cpu": [ "x64" ], @@ -5057,9 +4590,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.1.5.tgz", - "integrity": "sha512-3WeW328DN+xB5PZdhSWmqE+t3+44xWXEbqQ+caWJEZfOFdLp9yklBZEbVqVdqzznkoaXJYxTCp996KD6HmANeg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.2.2.tgz", + "integrity": "sha512-uW31JmfuPAaLUYW7NsEU8gzwgDAzpGPwjvkxnKlcWd8iDutoPKDJi8Wk9lFmPEZRxVSB0j1/wDQ7N2qliR9UFA==", "cpu": [ "arm" ], @@ -5071,9 +4604,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.1.5.tgz", - "integrity": "sha512-LAjaoOcBHGj6fiYB8ureiqPoph4eygbXu4vcOF+hsxiY74n8ilA7rJMmGUT0K0JOB5lmRQHSmor3mytRjS4qeQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.2.2.tgz", + "integrity": "sha512-4hdgZtWI1idQlWRp+eleWXD9KLvObgboRaVoBj2POdPEYvsKANllvMW0El8tEQwtw74yB9NT6P8ENBB5UJf5+g==", "cpu": [ "arm64" ], @@ -5085,9 +4618,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-x64": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.1.5.tgz", - "integrity": "sha512-k/IklElP70qdCXOQixclSl2GPLFiopynGoKX1FqDd1/H0E3Fo1oPwjY2rEVu+0nS3AOw1sryStdXk8CW3cVIsw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.2.2.tgz", + "integrity": "sha512-A0zjf4a2vM4B4GAx78ncuOTZ8Ka1DbTaG1Axf1e00Sa7f5coqlWiLg1PX7Gxvyibc2YqtqB+8tg1KKrE8guZVw==", "cpu": [ "x64" ], @@ -5099,9 +4632,9 @@ ] }, "node_modules/@lmdb/lmdb-win32-x64": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.1.5.tgz", - "integrity": "sha512-KYar6W8nraZfSJspcK7Kp7hdj238X/FNauYbZyrqPBrtsXI1hvI4/KcRcRGP50aQoV7fkKDyJERlrQGMGTZUsA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.2.2.tgz", + "integrity": "sha512-Y0qoSCAja+xZE7QQ0LCHoYAuyI1n9ZqukQJa8lv9X3yCvWahFF7OYHAgVH1ejp43XWstj3U89/PAAzcowgF/uQ==", "cpu": [ "x64" ], @@ -5623,9 +5156,9 @@ } }, "node_modules/@ng-select/ng-select": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-14.1.0.tgz", - "integrity": "sha512-cE/e7WIqLAgUF83mpmDWbgmy7OvzWTjCTjtcIzhabRbhN0RDqp7u39noC12kSN+viAfYnA1TS7rBru+IouNt1g==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-14.2.0.tgz", + "integrity": "sha512-Dq3PgOb0EBL31TV1Byr6RnQW/Vd5cdaaCEFbII2tIwiIT1r15oMdeSEZqzutuslBqgyggnponYAaBEgQBVorAg==", "license": "MIT", "dependencies": { "tslib": "^2.3.1" @@ -5656,9 +5189,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.0.4.tgz", - "integrity": "sha512-N3WCbQz5ipdAZoSWHNf81RLET6+isq35+GZu9u0StpFtJCpXAmRRAv4vdMUYL7DLOzRmvEgwww6Rd5AwGeLFSw==", + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.1.5.tgz", + "integrity": "sha512-oIpE5Ci/Gl2iZqa0Hs6IOxaXEDHkF/zisHcflzYGkMnYcSFj+wRgYEuBFaHLCwuxQf9OdGu31i05w849i6tY1Q==", "dev": true, "license": "MIT", "engines": { @@ -5668,7 +5201,7 @@ }, "peerDependencies": { "@angular/compiler-cli": "^19.0.0", - "typescript": ">=5.5 <5.7", + "typescript": ">=5.5 <5.8", "webpack": "^5.54.0" } }, @@ -6164,9 +5697,9 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", - "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6185,25 +5718,25 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.0", - "@parcel/watcher-darwin-arm64": "2.5.0", - "@parcel/watcher-darwin-x64": "2.5.0", - "@parcel/watcher-freebsd-x64": "2.5.0", - "@parcel/watcher-linux-arm-glibc": "2.5.0", - "@parcel/watcher-linux-arm-musl": "2.5.0", - "@parcel/watcher-linux-arm64-glibc": "2.5.0", - "@parcel/watcher-linux-arm64-musl": "2.5.0", - "@parcel/watcher-linux-x64-glibc": "2.5.0", - "@parcel/watcher-linux-x64-musl": "2.5.0", - "@parcel/watcher-win32-arm64": "2.5.0", - "@parcel/watcher-win32-ia32": "2.5.0", - "@parcel/watcher-win32-x64": "2.5.0" + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", - "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", "cpu": [ "arm64" ], @@ -6222,9 +5755,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", - "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", "cpu": [ "arm64" ], @@ -6243,9 +5776,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", - "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", "cpu": [ "x64" ], @@ -6264,9 +5797,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", - "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", "cpu": [ "x64" ], @@ -6285,9 +5818,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", - "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", "cpu": [ "arm" ], @@ -6306,9 +5839,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", - "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", "cpu": [ "arm" ], @@ -6327,9 +5860,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", - "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", "cpu": [ "arm64" ], @@ -6348,9 +5881,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", - "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", "cpu": [ "arm64" ], @@ -6369,9 +5902,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", - "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", "cpu": [ "x64" ], @@ -6390,9 +5923,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", - "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", "cpu": [ "x64" ], @@ -6411,9 +5944,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", - "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", "cpu": [ "arm64" ], @@ -6432,9 +5965,9 @@ } }, "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", - "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", "cpu": [ "ia32" ], @@ -6453,9 +5986,9 @@ } }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", - "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", "cpu": [ "x64" ], @@ -6487,14 +6020,6 @@ "node": ">=0.10" } }, - "node_modules/@parcel/watcher/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -6531,9 +6056,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz", - "integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.0.tgz", + "integrity": "sha512-Eeao7ewDq79jVEsrtWIj5RNqB8p2knlm9fhR6uJ2gqP7UfbLrTrxevudVrEPDM7Wkpn/HpRC2QfazH7MXLz3vQ==", "cpu": [ "arm" ], @@ -6545,9 +6070,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz", - "integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.0.tgz", + "integrity": "sha512-yVh0Kf1f0Fq4tWNf6mWcbQBCLDpDrDEl88lzPgKhrgTcDrTtlmun92ywEF9dCjmYO3EFiSuJeeo9cYRxl2FswA==", "cpu": [ "arm64" ], @@ -6559,9 +6084,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz", - "integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.0.tgz", + "integrity": "sha512-gCs0ErAZ9s0Osejpc3qahTsqIPUDjSKIyxK/0BGKvL+Tn0n3Kwvj8BrCv7Y5sR1Ypz1K2qz9Ny0VvkVyoXBVUQ==", "cpu": [ "arm64" ], @@ -6573,9 +6098,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz", - "integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.0.tgz", + "integrity": "sha512-aIB5Anc8hngk15t3GUkiO4pv42ykXHfmpXGS+CzM9CTyiWyT8HIS5ygRAy7KcFb/wiw4Br+vh1byqcHRTfq2tQ==", "cpu": [ "x64" ], @@ -6587,9 +6112,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz", - "integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.0.tgz", + "integrity": "sha512-kpdsUdMlVJMRMaOf/tIvxk8TQdzHhY47imwmASOuMajg/GXpw8GKNd8LNwIHE5Yd1onehNpcUB9jHY6wgw9nHQ==", "cpu": [ "arm64" ], @@ -6601,9 +6126,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz", - "integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.0.tgz", + "integrity": "sha512-D0RDyHygOBCQiqookcPevrvgEarN0CttBecG4chOeIYCNtlKHmf5oi5kAVpXV7qs0Xh/WO2RnxeicZPtT50V0g==", "cpu": [ "x64" ], @@ -6615,9 +6140,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz", - "integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.0.tgz", + "integrity": "sha512-mCIw8j5LPDXmCOW8mfMZwT6F/Kza03EnSr4wGYEswrEfjTfVsFOxvgYfuRMxTuUF/XmRb9WSMD5GhCWDe2iNrg==", "cpu": [ "arm" ], @@ -6629,9 +6154,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz", - "integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.0.tgz", + "integrity": "sha512-AwwldAu4aCJPob7zmjuDUMvvuatgs8B/QiVB0KwkUarAcPB3W+ToOT+18TQwY4z09Al7G0BvCcmLRop5zBLTag==", "cpu": [ "arm" ], @@ -6643,9 +6168,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz", - "integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.0.tgz", + "integrity": "sha512-e7kDUGVP+xw05pV65ZKb0zulRploU3gTu6qH1qL58PrULDGxULIS0OSDQJLH7WiFnpd3ZKUU4VM3u/Z7Zw+e7Q==", "cpu": [ "arm64" ], @@ -6657,9 +6182,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz", - "integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.0.tgz", + "integrity": "sha512-SXYJw3zpwHgaBqTXeAZ31qfW/v50wq4HhNVvKFhRr5MnptRX2Af4KebLWR1wpxGJtLgfS2hEPuALRIY3LPAAcA==", "cpu": [ "arm64" ], @@ -6670,10 +6195,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.0.tgz", + "integrity": "sha512-e5XiCinINCI4RdyU3sFyBH4zzz7LiQRvHqDtRe9Dt8o/8hTBaYpdPimayF00eY2qy5j4PaaWK0azRgUench6WQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz", - "integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.0.tgz", + "integrity": "sha512-3SWN3e0bAsm9ToprLFBSro8nJe6YN+5xmB11N4FfNf92wvLye/+Rh5JGQtKOpwLKt6e61R1RBc9g+luLJsc23A==", "cpu": [ "ppc64" ], @@ -6685,9 +6224,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz", - "integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.0.tgz", + "integrity": "sha512-B1Oqt3GLh7qmhvfnc2WQla4NuHlcxAD5LyueUi5WtMc76ZWY+6qDtQYqnxARx9r+7mDGfamD+8kTJO0pKUJeJA==", "cpu": [ "riscv64" ], @@ -6699,9 +6238,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz", - "integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.0.tgz", + "integrity": "sha512-UfUCo0h/uj48Jq2lnhX0AOhZPSTAq3Eostas+XZ+GGk22pI+Op1Y6cxQ1JkUuKYu2iU+mXj1QjPrZm9nNWV9rg==", "cpu": [ "s390x" ], @@ -6713,9 +6252,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz", - "integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.0.tgz", + "integrity": "sha512-chZLTUIPbgcpm+Z7ALmomXW8Zh+wE2icrG+K6nt/HenPLmtwCajhQC5flNSk1Xy5EDMt/QAOz2MhzfOfJOLSiA==", "cpu": [ "x64" ], @@ -6727,9 +6266,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz", - "integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.0.tgz", + "integrity": "sha512-jo0UolK70O28BifvEsFD/8r25shFezl0aUk2t0VJzREWHkq19e+pcLu4kX5HiVXNz5qqkD+aAq04Ct8rkxgbyQ==", "cpu": [ "x64" ], @@ -6741,9 +6280,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz", - "integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.0.tgz", + "integrity": "sha512-Vmg0NhAap2S54JojJchiu5An54qa6t/oKT7LmDaWggpIcaiL8WcWHEN6OQrfTdL6mQ2GFyH7j2T5/3YPEDOOGA==", "cpu": [ "arm64" ], @@ -6755,9 +6294,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz", - "integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.0.tgz", + "integrity": "sha512-CV2aqhDDOsABKHKhNcs1SZFryffQf8vK2XrxP6lxC99ELZAdvsDgPklIBfd65R8R+qvOm1SmLaZ/Fdq961+m7A==", "cpu": [ "ia32" ], @@ -6769,9 +6308,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz", - "integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.0.tgz", + "integrity": "sha512-g2ASy1QwHP88y5KWvblUolJz9rN+i4ZOsYzkEwcNfaNooxNUXG+ON6F5xFo0NIItpHqxcdAyls05VXpBnludGw==", "cpu": [ "x64" ], @@ -6783,14 +6322,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.0.4.tgz", - "integrity": "sha512-1fXBtkA/AjgMPxHLpGlw7NuT/wggCqAwBAmDnSiRnBBV7Pgs/tHorLgh7A9eoUi3c8CYCuAh8zqWNyjBGGigOQ==", + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.1.5.tgz", + "integrity": "sha512-Yks2QD87z2qJhVLi6O0tQDBG4pyX5n5c8BYEyZ+yiThjzIXBRkHjWS1jIFvd/y1+yU/NQFHYG/sy8sVOxfQ9IA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.0.4", - "@angular-devkit/schematics": "19.0.4", + "@angular-devkit/core": "19.1.5", + "@angular-devkit/schematics": "19.1.5", "jsonc-parser": "3.3.1" }, "engines": { @@ -6890,6 +6429,7 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -7119,9 +6659,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz", - "integrity": "sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dev": true, "license": "MIT", "dependencies": { @@ -7165,6 +6705,7 @@ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -7247,9 +6788,9 @@ } }, "node_modules/@types/qs": { - "version": "6.9.17", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", - "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", "dev": true, "license": "MIT" }, @@ -7323,9 +6864,9 @@ "dev": true }, "node_modules/@types/ws": { - "version": "8.5.13", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", - "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", "dev": true, "license": "MIT", "dependencies": { @@ -7554,161 +7095,176 @@ } }, "node_modules/@vitejs/plugin-basic-ssl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", - "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.2.0.tgz", + "integrity": "sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.6.0" + "node": ">=14.21.3" }, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -7716,13 +7272,15 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", @@ -7816,6 +7374,7 @@ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, + "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "regex-parser": "^2.2.11" @@ -7829,6 +7388,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -7839,14 +7399,11 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -7872,6 +7429,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -7889,6 +7447,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -7901,6 +7460,7 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -8034,6 +7594,13 @@ "dev": true, "license": "MIT" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -8068,6 +7635,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", @@ -8290,9 +7858,9 @@ "license": "MIT" }, "node_modules/beasties": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.1.0.tgz", - "integrity": "sha512-+Ssscd2gVG24qRNC+E2g88D+xsQW4xwakWtKAiGEQ3Pw54/FGdyo9RrfxhGhEv6ilFVbB7r3Lgx+QnAxnSpECw==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.2.0.tgz", + "integrity": "sha512-Ljqskqx/tbZagIglYoJIMzH5zgssyp+in9+9sAyh15N22AornBeIDnb8EZ6Rk+6ShfMxd92uO3gfpT0NtZbpow==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -8300,10 +7868,13 @@ "css-what": "^6.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", - "htmlparser2": "^9.0.0", + "htmlparser2": "^9.1.0", "picocolors": "^1.1.1", - "postcss": "^8.4.47", + "postcss": "^8.4.49", "postcss-media-query-parser": "^0.2.3" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/before-after-hook": { @@ -8317,6 +7888,7 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -8445,9 +8017,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -8462,10 +8034,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -8480,6 +8053,7 @@ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, + "license": "MIT", "dependencies": { "fast-json-stable-stringify": "2.x" }, @@ -8733,6 +8307,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -8752,9 +8357,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001676", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", - "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==", + "version": "1.0.30001696", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz", + "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", "funding": [ { "type": "opencollective", @@ -8768,7 +8373,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/canvas": { "version": "2.11.2", @@ -9238,6 +8844,7 @@ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, + "license": "MIT", "dependencies": { "is-what": "^3.14.1" }, @@ -9250,6 +8857,7 @@ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "^3.3.2", "glob-parent": "^6.0.1", @@ -9274,6 +8882,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -9282,13 +8891,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.3" }, "funding": { "type": "opencollective", @@ -9307,6 +8916,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, + "license": "MIT", "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -9332,13 +8942,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/cosmiconfig/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -9392,6 +9004,7 @@ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -9457,6 +9070,7 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, + "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -9805,9 +9419,9 @@ } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9819,6 +9433,21 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -9833,10 +9462,27 @@ "dev": true, "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.5.50", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", - "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==" + "version": "1.5.90", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.90.tgz", + "integrity": "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==", + "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", @@ -9860,6 +9506,7 @@ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -9966,6 +9613,7 @@ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "prr": "~1.0.1" @@ -9984,13 +9632,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -10010,48 +9656,64 @@ "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, "node_modules/esbuild-wasm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.24.0.tgz", - "integrity": "sha512-xhNn5tL1AhkPg4ft59yXT6FkwKXiPSYyz1IeinJHUJpjvOHOIPvdmFQc0pGdjxlKSbzZc2mNmtVOWAR1EF/JAg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.24.2.tgz", + "integrity": "sha512-03/7Z1gD+ohDnScFztvI4XddTAbKVmMEzCvvkBpQdWKEXJ+73dTyeNrmdxP1Q0zpDMFjzUJwtK4rLjqwiHbzkw==", "dev": true, "license": "MIT", "bin": { @@ -10397,7 +10059,8 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/events": { "version": "3.3.0", @@ -10559,15 +10222,16 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -10638,6 +10302,39 @@ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -10763,6 +10460,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -10831,6 +10529,7 @@ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, @@ -10950,16 +10649,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10977,6 +10682,20 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -11046,6 +10765,7 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", @@ -11066,6 +10786,7 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -11074,12 +10795,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11125,23 +10847,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11158,10 +10869,11 @@ "peer": true }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -11254,23 +10966,6 @@ "node": ">=12" } }, - "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -11329,9 +11024,9 @@ } }, "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", "dev": true, "license": "MIT" }, @@ -11340,6 +11035,7 @@ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, + "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -11380,6 +11076,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz", "integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.15", "debug": "^4.3.6", @@ -11397,18 +11094,19 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -11452,6 +11150,7 @@ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -11532,6 +11231,7 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", "dev": true, + "license": "MIT", "optional": true, "bin": { "image-size": "bin/image-size.js" @@ -11689,12 +11389,16 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11863,7 +11567,8 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-wsl": { "version": "2.2.0", @@ -11998,6 +11703,25 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -12348,17 +12072,17 @@ } }, "node_modules/jest-preset-angular": { - "version": "14.4.2", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.4.2.tgz", - "integrity": "sha512-BYYv0FaTDfBNh8WyA9mpOV3krfw20kurBGK8INZUnv7KZDAWZuQtCET4TwTWxSNQ9jS1OX1+a5weCm/bTDDM1A==", + "version": "14.5.1", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.5.1.tgz", + "integrity": "sha512-HLYYMwNcv3mFrKbOPJwR29tKqVg+yWxez8ilCIsEj1HRYZ/OVsBy5+dcMok+VqL5nmeukTsGnEfGWt+SsQqtkA==", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "esbuild-wasm": ">=0.15.13", - "jest-environment-jsdom": "^29.0.0", - "jest-util": "^29.0.0", - "pretty-format": "^29.0.0", + "jest-environment-jsdom": "^29.7.0", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0", "ts-jest": "^29.0.0" }, "engines": { @@ -12372,7 +12096,13 @@ "@angular/core": ">=15.0.0 <20.0.0", "@angular/platform-browser-dynamic": ">=15.0.0 <20.0.0", "jest": "^29.0.0", + "jsdom": ">=20.0.0", "typescript": ">=4.8" + }, + "peerDependenciesMeta": { + "jsdom": { + "optional": true + } } }, "node_modules/jest-regex-util": { @@ -12860,6 +12590,7 @@ "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", "dev": true, + "license": "MIT", "dependencies": { "source-map-support": "^0.5.5" } @@ -12912,10 +12643,11 @@ } }, "node_modules/less": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", - "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.1.tgz", + "integrity": "sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -12942,6 +12674,7 @@ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18.12.0" }, @@ -12968,6 +12701,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "pify": "^4.0.1", @@ -12982,6 +12716,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "optional": true, "bin": { "semver": "bin/semver" @@ -12992,6 +12727,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -13024,6 +12760,7 @@ "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", "dev": true, + "license": "ISC", "dependencies": { "webpack-sources": "^3.0.0" }, @@ -13147,9 +12884,9 @@ } }, "node_modules/lmdb": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.1.5.tgz", - "integrity": "sha512-46Mch5Drq+A93Ss3gtbg+Xuvf5BOgIuvhKDWoGa3HcPHI6BL2NCOkRdSx1D4VfzwrxhnsjbyIVsLRlQHu6URvw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.2.2.tgz", + "integrity": "sha512-LriG93la4PbmPMwI7Hbv8W+0ncLK7549w4sbZSi4QGDjnnxnmNMgxUkaQTEMzH8TpwsfFvgEjpLX7V8B/I9e3g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -13165,14 +12902,22 @@ "download-lmdb-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "3.1.5", - "@lmdb/lmdb-darwin-x64": "3.1.5", - "@lmdb/lmdb-linux-arm": "3.1.5", - "@lmdb/lmdb-linux-arm64": "3.1.5", - "@lmdb/lmdb-linux-x64": "3.1.5", - "@lmdb/lmdb-win32-x64": "3.1.5" + "@lmdb/lmdb-darwin-arm64": "3.2.2", + "@lmdb/lmdb-darwin-x64": "3.2.2", + "@lmdb/lmdb-linux-arm": "3.2.2", + "@lmdb/lmdb-linux-arm64": "3.2.2", + "@lmdb/lmdb-linux-x64": "3.2.2", + "@lmdb/lmdb-win32-x64": "3.2.2" } }, + "node_modules/lmdb/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -13187,6 +12932,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12.13.0" } @@ -13226,7 +12972,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -13487,9 +13234,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, "license": "MIT", "dependencies": { @@ -13554,6 +13301,16 @@ "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -13565,9 +13322,9 @@ } }, "node_modules/memfs": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.1.tgz", - "integrity": "sha512-Fq5CMEth+2iprLJ5mNizRcWuiwRZYjNkUD0zKk224jZunE9CRacTRDK8QLALbMBlNX2y3nY6lKZbesCwDwacig==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -13646,6 +13403,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -14120,9 +13878,9 @@ "peer": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -14130,6 +13888,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -14155,6 +13914,7 @@ "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "iconv-lite": "^0.6.3", @@ -14172,6 +13932,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -14237,9 +13998,9 @@ } }, "node_modules/ngx-cookie-service": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-19.0.0.tgz", - "integrity": "sha512-itxGY1BlIRoEjEtDsSsRKnJuiQteTMLKPNHrykiH06tjUQ1bi3orE7YKU1D210VBqVy1jNrB7hKuGOOIQtQJDA==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-19.1.0.tgz", + "integrity": "sha512-EddQiinQ/EicUp4Lg/j0RwTsAJp7FCAGv+Z5dfwll/BoROb7hTnf7Suqbx2xZFNM4YqLXpZfXlAe8zig9cBfhw==", "license": "MIT", "dependencies": { "tslib": "^2.8.0" @@ -14278,6 +14039,21 @@ "@angular/core": ">=14.0.0" } }, + "node_modules/ngx-ui-tour-core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-14.0.0.tgz", + "integrity": "sha512-6pzzEwxn/gCS3puEXDqgINBRbhvhzHYjmiA9DTCNEx1dPfYwjZVmPqNvNeZIVHucVnVZViAAKvA6MTc3Gm7aOw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/router": "^19.0.0", + "rxjs": "^7.4.0" + } + }, "node_modules/ngx-ui-tour-ng-bootstrap": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/ngx-ui-tour-ng-bootstrap/-/ngx-ui-tour-ng-bootstrap-16.0.0.tgz", @@ -14293,21 +14069,6 @@ "@ng-bootstrap/ng-bootstrap": "^18.0.0" } }, - "node_modules/ngx-ui-tour-ng-bootstrap/node_modules/ngx-ui-tour-core": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-14.0.0.tgz", - "integrity": "sha512-6pzzEwxn/gCS3puEXDqgINBRbhvhzHYjmiA9DTCNEx1dPfYwjZVmPqNvNeZIVHucVnVZViAAKvA6MTc3Gm7aOw==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": "^19.0.0", - "@angular/core": "^19.0.0", - "@angular/router": "^19.0.0", - "rxjs": "^7.4.0" - } - }, "node_modules/node-abi": { "version": "3.71.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", @@ -14322,10 +14083,9 @@ } }, "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", - "dev": true, + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "license": "MIT", "optional": true }, @@ -14594,9 +14354,10 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" }, "node_modules/nopt": { "version": "8.0.0", @@ -14643,6 +14404,7 @@ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -14684,9 +14446,9 @@ } }, "node_modules/npm-package-arg": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.0.tgz", - "integrity": "sha512-ZTE0hbwSdTNL+Stx2zxSqdu2KZfNDcrtrLdIk7XGnQFYBWYDho/ORvXtn5XEePcL3tFpGjHCV3X3xrtDh7eZ+A==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.1.tgz", + "integrity": "sha512-aDxjFfPV3Liw0WOBWlyZLMBqtbgbg03rmGvHDJa2Ttv7tIz+1oB5qWec4psCDFZcZi9b5XdGkPdQiJxOPzvQRQ==", "dev": true, "license": "ISC", "dependencies": { @@ -15239,6 +15001,7 @@ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -15444,6 +15207,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -15490,13 +15254,6 @@ "node": "^18.12.0 || >= 20.9.0" } }, - "node_modules/pdfjs-dist/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT", - "optional": true - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -15520,6 +15277,7 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=6" @@ -15535,9 +15293,9 @@ } }, "node_modules/piscina": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.7.0.tgz", - "integrity": "sha512-b8hvkpp9zS0zsfa939b/jXbe64Z2gZv0Ha7FYPNUiDIB1y2AtxcOZdfP8xN8HFjUaqQiT9gRlfjAsoL8vdJ1Iw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.8.0.tgz", + "integrity": "sha512-EZJb+ZxDrQf3dihsUL7p42pjNyrNIFJCrRHPMgxu/svsj+P3xS3fuEWp7k2+rfsavfl1N0G29b1HGs7J0m8rZA==", "dev": true, "license": "MIT", "optionalDependencies": { @@ -15726,6 +15484,7 @@ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "dev": true, + "license": "MIT", "dependencies": { "cosmiconfig": "^9.0.0", "jiti": "^1.20.0", @@ -15764,6 +15523,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -15772,13 +15532,14 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "engines": { @@ -15789,12 +15550,13 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, + "license": "ISC", "dependencies": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": "^10 || ^12 || >= 14" @@ -15808,6 +15570,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, + "license": "ISC", "dependencies": { "icss-utils": "^5.0.0" }, @@ -15819,10 +15582,11 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -15835,7 +15599,8 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/prebuild-install": { "version": "7.1.2", @@ -16068,6 +15833,7 @@ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/psl": { @@ -16300,7 +16066,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regexpu-core": { "version": "6.2.0", @@ -16364,18 +16131,22 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16406,6 +16177,7 @@ "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, + "license": "MIT", "dependencies": { "adjust-sourcemap-loader": "^4.0.0", "convert-source-map": "^1.7.0", @@ -16422,6 +16194,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -16436,6 +16209,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -16506,9 +16280,9 @@ } }, "node_modules/rollup": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz", - "integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.0.tgz", + "integrity": "sha512-+4C/cgJ9w6sudisA0nZz0+O7lTP9a3CzNLsoDwaRumM8QHwghUsu6tqHXiTmNUp/rqNiM14++7dkzHDyCRs0Jg==", "dev": true, "license": "MIT", "dependencies": { @@ -16522,24 +16296,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.26.0", - "@rollup/rollup-android-arm64": "4.26.0", - "@rollup/rollup-darwin-arm64": "4.26.0", - "@rollup/rollup-darwin-x64": "4.26.0", - "@rollup/rollup-freebsd-arm64": "4.26.0", - "@rollup/rollup-freebsd-x64": "4.26.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.26.0", - "@rollup/rollup-linux-arm-musleabihf": "4.26.0", - "@rollup/rollup-linux-arm64-gnu": "4.26.0", - "@rollup/rollup-linux-arm64-musl": "4.26.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.26.0", - "@rollup/rollup-linux-riscv64-gnu": "4.26.0", - "@rollup/rollup-linux-s390x-gnu": "4.26.0", - "@rollup/rollup-linux-x64-gnu": "4.26.0", - "@rollup/rollup-linux-x64-musl": "4.26.0", - "@rollup/rollup-win32-arm64-msvc": "4.26.0", - "@rollup/rollup-win32-ia32-msvc": "4.26.0", - "@rollup/rollup-win32-x64-msvc": "4.26.0", + "@rollup/rollup-android-arm-eabi": "4.34.0", + "@rollup/rollup-android-arm64": "4.34.0", + "@rollup/rollup-darwin-arm64": "4.34.0", + "@rollup/rollup-darwin-x64": "4.34.0", + "@rollup/rollup-freebsd-arm64": "4.34.0", + "@rollup/rollup-freebsd-x64": "4.34.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.0", + "@rollup/rollup-linux-arm-musleabihf": "4.34.0", + "@rollup/rollup-linux-arm64-gnu": "4.34.0", + "@rollup/rollup-linux-arm64-musl": "4.34.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.0", + "@rollup/rollup-linux-riscv64-gnu": "4.34.0", + "@rollup/rollup-linux-s390x-gnu": "4.34.0", + "@rollup/rollup-linux-x64-gnu": "4.34.0", + "@rollup/rollup-linux-x64-musl": "4.34.0", + "@rollup/rollup-win32-arm64-msvc": "4.34.0", + "@rollup/rollup-win32-ia32-msvc": "4.34.0", + "@rollup/rollup-win32-x64-msvc": "4.34.0", "fsevents": "~2.3.2" } }, @@ -16613,9 +16388,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.80.7", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.7.tgz", - "integrity": "sha512-MVWvN0u5meytrSjsU7AWsbhoXi1sc58zADXFllfZzbsBT1GHjjar6JwBINYPRrkx/zqnQ6uqbQuHgE95O+C+eQ==", + "version": "1.83.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.1.tgz", + "integrity": "sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==", "dev": true, "license": "MIT", "dependencies": { @@ -16634,9 +16409,9 @@ } }, "node_modules/sass-loader": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.3.tgz", - "integrity": "sha512-gosNorT1RCkuCMyihv6FBRR7BMV06oKRAs+l4UMp1mlcVg9rWN6KMmUj3igjQwmYys4mDP3etEYJgiHRbgHCHA==", + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz", + "integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==", "dev": true, "license": "MIT", "dependencies": { @@ -16679,6 +16454,7 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true, + "license": "ISC", "optional": true }, "node_modules/saxes": { @@ -16694,10 +16470,11 @@ } }, "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -16705,7 +16482,7 @@ "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -16986,16 +16763,73 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -17194,6 +17028,7 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -17203,6 +17038,7 @@ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dev": true, + "license": "MIT", "dependencies": { "iconv-lite": "^0.6.3", "source-map-js": "^1.0.2" @@ -17223,6 +17059,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -17615,9 +17452,9 @@ "dev": true }, "node_modules/terser": { - "version": "5.36.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", - "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -17882,6 +17719,7 @@ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, + "license": "MIT", "bin": { "tree-kill": "cli.js" } @@ -17899,19 +17737,21 @@ } }, "node_modules/ts-jest": { - "version": "29.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", - "integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==", + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", "dev": true, + "license": "MIT", "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" @@ -18106,7 +17946,8 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/typescript": { "version": "5.5.4", @@ -18188,6 +18029,7 @@ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -18410,21 +18252,21 @@ } }, "node_modules/vite": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", - "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz", + "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.24.2", + "postcss": "^8.4.49", + "rollup": "^4.23.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -18433,19 +18275,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.4.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -18466,6 +18314,12 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, @@ -18541,17 +18395,17 @@ } }, "node_modules/webpack": { - "version": "5.96.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", - "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.14.0", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", @@ -18618,9 +18472,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.1.0.tgz", - "integrity": "sha512-aQpaN81X6tXie1FoOB7xlMfCsN19pSvRAeYUHOdFWOlhpQ/LlbfTqYwwmEDFV0h8GGuqmCmKmT+pxcUV/Nt2gQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz", + "integrity": "sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA==", "dev": true, "license": "MIT", "dependencies": { @@ -18637,10 +18491,9 @@ "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", - "express": "^4.19.2", + "express": "^4.21.2", "graceful-fs": "^4.2.6", - "html-entities": "^2.4.0", - "http-proxy-middleware": "^2.0.3", + "http-proxy-middleware": "^2.0.7", "ipaddr.js": "^2.1.0", "launch-editor": "^2.6.1", "open": "^10.0.3", @@ -18779,6 +18632,7 @@ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", "dev": true, + "license": "MIT", "dependencies": { "typed-assert": "^1.0.8" }, @@ -19079,10 +18933,14 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/src-ui/package.json b/src-ui/package.json index 0e19f4240..d61178398 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -11,17 +11,17 @@ }, "private": true, "dependencies": { - "@angular/cdk": "^19.0.2", - "@angular/common": "~19.0.3", - "@angular/compiler": "~19.0.3", - "@angular/core": "~19.0.3", - "@angular/forms": "~19.0.3", - "@angular/localize": "~19.0.3", - "@angular/platform-browser": "~19.0.3", - "@angular/platform-browser-dynamic": "~19.0.3", - "@angular/router": "~19.0.3", + "@angular/cdk": "^19.1.2", + "@angular/common": "~19.1.4", + "@angular/compiler": "~19.1.4", + "@angular/core": "~19.1.4", + "@angular/forms": "~19.1.4", + "@angular/localize": "~19.1.4", + "@angular/platform-browser": "~19.1.4", + "@angular/platform-browser-dynamic": "~19.1.4", + "@angular/router": "~19.1.4", "@ng-bootstrap/ng-bootstrap": "^18.0.0", - "@ng-select/ng-select": "^14.1.0", + "@ng-select/ng-select": "^14.2.0", "@ngneat/dirty-check-forms": "^3.0.3", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.3", @@ -30,7 +30,7 @@ "ng2-pdf-viewer": "^10.4.0", "ngx-bootstrap-icons": "^1.9.3", "ngx-color": "^9.0.0", - "ngx-cookie-service": "^19.0.0", + "ngx-cookie-service": "^19.1.0", "ngx-device-detector": "^9.0.0", "ngx-file-drop": "^16.0.0", "ngx-ui-tour-ng-bootstrap": "^16.0.0", @@ -41,18 +41,18 @@ "zone.js": "^0.15.0" }, "devDependencies": { - "@angular-builders/custom-webpack": "^19.0.0-beta.0", - "@angular-builders/jest": "^19.0.0-beta.1", + "@angular-builders/custom-webpack": "^19.0.0", + "@angular-builders/jest": "^19.0.0", "@angular-devkit/build-angular": "^19.0.4", - "@angular-devkit/core": "^19.0.4", - "@angular-devkit/schematics": "^19.0.4", - "@angular-eslint/builder": "19.0.0", - "@angular-eslint/eslint-plugin": "19.0.0", - "@angular-eslint/eslint-plugin-template": "19.0.0", - "@angular-eslint/schematics": "19.0.0", - "@angular-eslint/template-parser": "19.0.0", - "@angular/cli": "~19.0.4", - "@angular/compiler-cli": "~19.0.3", + "@angular-devkit/core": "^19.1.5", + "@angular-devkit/schematics": "^19.1.5", + "@angular-eslint/builder": "19.0.2", + "@angular-eslint/eslint-plugin": "19.0.2", + "@angular-eslint/eslint-plugin-template": "19.0.2", + "@angular-eslint/schematics": "19.0.2", + "@angular-eslint/template-parser": "19.0.2", + "@angular/cli": "~19.1.5", + "@angular/compiler-cli": "~19.1.4", "@codecov/webpack-plugin": "^1.2.1", "@playwright/test": "^1.48.2", "@types/jest": "^29.5.14", From 4cd755f641c32f6181b4d2e58875ea9f8c4f7bec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 01:07:16 +0000 Subject: [PATCH 19/37] Chore(deps-dev): Bump the frontend-eslint-dependencies group (#8988) Bumps the frontend-eslint-dependencies group in /src-ui with 4 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser), [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) and [eslint](https://github.com/eslint/eslint). Updates `@typescript-eslint/eslint-plugin` from 8.12.2 to 8.22.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.22.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.12.2 to 8.22.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.22.0/packages/parser) Updates `@typescript-eslint/utils` from 8.12.2 to 8.22.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.22.0/packages/utils) Updates `eslint` from 9.14.0 to 9.19.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v9.14.0...v9.19.0) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-eslint-dependencies - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-eslint-dependencies - dependency-name: "@typescript-eslint/utils" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-eslint-dependencies - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-eslint-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 268 +++++++++++++++++++++------------------ src-ui/package.json | 6 +- 2 files changed, 151 insertions(+), 123 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 9ad8cf38e..002b9bab0 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -55,10 +55,10 @@ "@playwright/test": "^1.48.2", "@types/jest": "^29.5.14", "@types/node": "^22.8.6", - "@typescript-eslint/eslint-plugin": "^8.12.2", - "@typescript-eslint/parser": "^8.12.2", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "@typescript-eslint/utils": "^8.0.0", - "eslint": "^9.14.0", + "eslint": "^9.19.0", "jest": "29.7.0", "jest-environment-jsdom": "^29.7.0", "jest-preset-angular": "^14.4.2", @@ -3504,12 +3504,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -3518,19 +3519,24 @@ } }, "node_modules/@eslint/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", - "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -3554,6 +3560,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3569,13 +3576,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -3588,6 +3597,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -3599,32 +3609,37 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", - "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", + "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, + "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -3689,10 +3704,11 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz", - "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -6889,20 +6905,21 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", - "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/type-utils": "8.12.2", - "@typescript-eslint/utils": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6913,24 +6930,21 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", - "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4" }, "engines": { @@ -6941,22 +6955,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", - "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2" + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6967,15 +6978,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", - "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/utils": "8.12.2", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6984,17 +6996,17 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", - "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -7004,19 +7016,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", - "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7025,10 +7038,8 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -7036,6 +7047,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -7045,6 +7057,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7056,15 +7069,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", - "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2" + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7074,17 +7088,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", - "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.12.2", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.22.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7094,6 +7110,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitejs/plugin-basic-ssl": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.2.0.tgz", @@ -7356,6 +7385,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -8986,10 +9016,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -9770,26 +9801,27 @@ } }, "node_modules/eslint": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", - "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", + "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.7.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.14.0", - "@eslint/plugin-kit": "^0.2.0", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.10.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.19.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.0", + "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", @@ -9808,8 +9840,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -9966,6 +9997,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", @@ -9983,6 +10015,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -17596,12 +17629,6 @@ "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "node_modules/thingies": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", @@ -17725,15 +17752,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-jest": { diff --git a/src-ui/package.json b/src-ui/package.json index d61178398..434f58daf 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -57,10 +57,10 @@ "@playwright/test": "^1.48.2", "@types/jest": "^29.5.14", "@types/node": "^22.8.6", - "@typescript-eslint/eslint-plugin": "^8.12.2", - "@typescript-eslint/parser": "^8.12.2", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "@typescript-eslint/utils": "^8.0.0", - "eslint": "^9.14.0", + "eslint": "^9.19.0", "jest": "29.7.0", "jest-environment-jsdom": "^29.7.0", "jest-preset-angular": "^14.4.2", From 7df0b621a56c8758216c55ea4467ee5b89531e87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 01:21:35 +0000 Subject: [PATCH 20/37] Chore(deps-dev): Bump @types/node from 22.8.6 to 22.13.0 in /src-ui (#8989) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.8.6 to 22.13.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 20 +++++++++++--------- src-ui/package.json | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 002b9bab0..dc3b6b894 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -54,7 +54,7 @@ "@codecov/webpack-plugin": "^1.2.1", "@playwright/test": "^1.48.2", "@types/jest": "^29.5.14", - "@types/node": "^22.8.6", + "@types/node": "^22.13.0", "@typescript-eslint/eslint-plugin": "^8.22.0", "@typescript-eslint/parser": "^8.22.0", "@typescript-eslint/utils": "^8.0.0", @@ -6785,12 +6785,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", + "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/node-forge": { @@ -18003,10 +18004,11 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", diff --git a/src-ui/package.json b/src-ui/package.json index 434f58daf..0e0013efe 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -56,7 +56,7 @@ "@codecov/webpack-plugin": "^1.2.1", "@playwright/test": "^1.48.2", "@types/jest": "^29.5.14", - "@types/node": "^22.8.6", + "@types/node": "^22.13.0", "@typescript-eslint/eslint-plugin": "^8.22.0", "@typescript-eslint/parser": "^8.22.0", "@typescript-eslint/utils": "^8.0.0", From 438650bf1773453fdcb4093ff36fb0ac87922015 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 01:35:21 +0000 Subject: [PATCH 21/37] Chore(deps-dev): Bump @playwright/test from 1.48.2 to 1.50.1 in /src-ui (#8993) Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.48.2 to 1.50.1. - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.48.2...v1.50.1) --- updated-dependencies: - dependency-name: "@playwright/test" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 28 ++++++++++++++++------------ src-ui/package.json | 2 +- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index dc3b6b894..19de9f483 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -52,7 +52,7 @@ "@angular/cli": "~19.1.5", "@angular/compiler-cli": "~19.1.4", "@codecov/webpack-plugin": "^1.2.1", - "@playwright/test": "^1.48.2", + "@playwright/test": "^1.50.1", "@types/jest": "^29.5.14", "@types/node": "^22.13.0", "@typescript-eslint/eslint-plugin": "^8.22.0", @@ -6048,12 +6048,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.48.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", - "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", + "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright": "1.48.2" + "playwright": "1.50.1" }, "bin": { "playwright": "cli.js" @@ -15441,12 +15442,13 @@ } }, "node_modules/playwright": { - "version": "1.48.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", - "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", + "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.2" + "playwright-core": "1.50.1" }, "bin": { "playwright": "cli.js" @@ -15459,10 +15461,11 @@ } }, "node_modules/playwright-core": { - "version": "1.48.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", - "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", + "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", "dev": true, + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, @@ -15476,6 +15479,7 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" diff --git a/src-ui/package.json b/src-ui/package.json index 0e0013efe..d5b5c20ae 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -54,7 +54,7 @@ "@angular/cli": "~19.1.5", "@angular/compiler-cli": "~19.1.4", "@codecov/webpack-plugin": "^1.2.1", - "@playwright/test": "^1.48.2", + "@playwright/test": "^1.50.1", "@types/jest": "^29.5.14", "@types/node": "^22.13.0", "@typescript-eslint/eslint-plugin": "^8.22.0", From 2e5656e1cea259f2187039d261ff4df7d2cbc645 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 01:46:24 +0000 Subject: [PATCH 22/37] Chore(deps-dev): Bump @codecov/webpack-plugin in /src-ui (#8991) Bumps @codecov/webpack-plugin from 1.2.1 to 1.8.0. --- updated-dependencies: - dependency-name: "@codecov/webpack-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 106 +++++++++++++++++++++++++-------------- src-ui/package.json | 2 +- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 19de9f483..a1241785a 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -51,7 +51,7 @@ "@angular-eslint/template-parser": "19.0.2", "@angular/cli": "~19.1.5", "@angular/compiler-cli": "~19.1.4", - "@codecov/webpack-plugin": "^1.2.1", + "@codecov/webpack-plugin": "^1.8.0", "@playwright/test": "^1.50.1", "@types/jest": "^29.5.14", "@types/node": "^22.13.0", @@ -83,6 +83,7 @@ "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", "dev": true, + "license": "MIT", "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" @@ -93,6 +94,7 @@ "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", "dev": true, + "license": "MIT", "dependencies": { "@actions/io": "^1.0.1" } @@ -102,6 +104,7 @@ "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.0.tgz", "integrity": "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==", "dev": true, + "license": "MIT", "dependencies": { "@actions/http-client": "^2.2.0", "@octokit/core": "^5.0.1", @@ -114,6 +117,7 @@ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", "dev": true, + "license": "MIT", "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" @@ -123,7 +127,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@ampproject/remapping": { "version": "2.3.0", @@ -2982,13 +2987,15 @@ "dev": true }, "node_modules/@codecov/bundler-plugin-core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@codecov/bundler-plugin-core/-/bundler-plugin-core-1.2.1.tgz", - "integrity": "sha512-9Iqr+PAQ/QVEnvKr56W/Jo8IaWJDFQkbcffNgG8Oc5GIQ1NRWsPmL/jxhkGrnp2LMJnpG2gPtV3+fet1jy6gbA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@codecov/bundler-plugin-core/-/bundler-plugin-core-1.8.0.tgz", + "integrity": "sha512-D1aeA8u3RHOkQVLImLHxW6zFdUrw5wgoeDzYrKZeDExGp5ePs4RpJxKwklg1N0e1JxRcAgKj+zZo/y3Q4nV+sA==", "dev": true, + "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", + "@sentry/core": "^8.42.0", "chalk": "4.1.2", "semver": "^7.5.4", "unplugin": "^1.10.1", @@ -2999,12 +3006,13 @@ } }, "node_modules/@codecov/webpack-plugin": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@codecov/webpack-plugin/-/webpack-plugin-1.2.1.tgz", - "integrity": "sha512-puA2Zb9A6e5sZ3AGpoYyymC64qgp0uUzDjnEa3EWtD/bQKa+m2HDu71sXUyJU/QFrWWa8xNaju4SPNVJU0m5EQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@codecov/webpack-plugin/-/webpack-plugin-1.8.0.tgz", + "integrity": "sha512-G62kbpAFuutQIrbuw97MAf5vkgIKXf64Nj0pRe6OPbC5Aps0nWrEL9v0Sr0WPdHcbQnr3i6noIXyUDdV1lUZ5g==", "dev": true, + "license": "MIT", "dependencies": { - "@codecov/bundler-plugin-core": "^1.2.1", + "@codecov/bundler-plugin-core": "^1.8.0", "unplugin": "^1.10.1" }, "engines": { @@ -3651,6 +3659,7 @@ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" } @@ -5559,6 +5568,7 @@ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18" } @@ -5568,6 +5578,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -5586,6 +5597,7 @@ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz", "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" @@ -5599,6 +5611,7 @@ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/request": "^8.3.0", "@octokit/types": "^13.0.0", @@ -5609,16 +5622,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", - "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", - "dev": true + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", + "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "dev": true, + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz", "integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^12.6.0" }, @@ -5633,13 +5648,15 @@ "version": "20.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" } @@ -5649,6 +5666,7 @@ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^12.6.0" }, @@ -5663,13 +5681,15 @@ "version": "20.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" } @@ -5679,6 +5699,7 @@ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz", "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/endpoint": "^9.0.1", "@octokit/request-error": "^5.1.0", @@ -5694,6 +5715,7 @@ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", @@ -5704,12 +5726,13 @@ } }, "node_modules/@octokit/types": { - "version": "13.6.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz", - "integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==", + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", + "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^22.2.0" + "@octokit/openapi-types": "^23.0.1" } }, "node_modules/@parcel/watcher": { @@ -6355,6 +6378,16 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@sentry/core": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.53.0.tgz", + "integrity": "sha512-u6p5JeGSgvcoDqVcPve2gcJuhks8EQXPELzeYKuW3rHpsUfkLG6X5RVtk32dKOqqL2qzvMelnknBN7tyIf5PiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.18" + } + }, "node_modules/@sigstore/bundle": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-3.0.0.tgz", @@ -7913,7 +7946,8 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/big.js": { "version": "5.2.2", @@ -9325,7 +9359,8 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/destroy": { "version": "1.2.0", @@ -17911,6 +17946,7 @@ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } @@ -18101,7 +18137,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/universalify": { "version": "2.0.1", @@ -18123,24 +18160,17 @@ } }, "node_modules/unplugin": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.15.0.tgz", - "integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", "dev": true, + "license": "MIT", "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" }, "engines": { "node": ">=14.0.0" - }, - "peerDependencies": { - "webpack-sources": "^3" - }, - "peerDependenciesMeta": { - "webpack-sources": { - "optional": true - } } }, "node_modules/update-browserslist-db": { @@ -18687,7 +18717,8 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/webpack/node_modules/ajv": { "version": "6.12.6", @@ -19039,10 +19070,11 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src-ui/package.json b/src-ui/package.json index d5b5c20ae..74299dce9 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -53,7 +53,7 @@ "@angular-eslint/template-parser": "19.0.2", "@angular/cli": "~19.1.5", "@angular/compiler-cli": "~19.1.4", - "@codecov/webpack-plugin": "^1.2.1", + "@codecov/webpack-plugin": "^1.8.0", "@playwright/test": "^1.50.1", "@types/jest": "^29.5.14", "@types/node": "^22.13.0", From 3e848e6e0fd3ca71b35d09646b8990229dc587fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 01:55:54 +0000 Subject: [PATCH 23/37] Chore(deps): Bump uuid from 11.0.2 to 11.0.5 in /src-ui (#8992) Bumps [uuid](https://github.com/uuidjs/uuid) from 11.0.2 to 11.0.5. - [Release notes](https://github.com/uuidjs/uuid/releases) - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md) - [Commits](https://github.com/uuidjs/uuid/compare/v11.0.2...v11.0.5) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 9 +++++---- src-ui/package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index a1241785a..37314ebb1 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -35,7 +35,7 @@ "rxjs": "^7.8.1", "tslib": "^2.8.1", "utif": "^3.1.0", - "uuid": "^11.0.2", + "uuid": "^11.0.5", "zone.js": "^0.15.0" }, "devDependencies": { @@ -18247,13 +18247,14 @@ } }, "node_modules/uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/esm/bin/uuid" } diff --git a/src-ui/package.json b/src-ui/package.json index 74299dce9..1aecf9c06 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -37,7 +37,7 @@ "rxjs": "^7.8.1", "tslib": "^2.8.1", "utif": "^3.1.0", - "uuid": "^11.0.2", + "uuid": "^11.0.5", "zone.js": "^0.15.0" }, "devDependencies": { From e877beea4eab0119d9d6d5710d47fbe69675656a Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:01:49 -0800 Subject: [PATCH 24/37] Fix: use button toolbar for management lists for better wrapping --- src-ui/messages.xlf | 56 +++++++++---------- .../management-list.component.html | 52 ++++++++--------- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 7499a56ff..29852f89a 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -332,19 +332,19 @@ src/app/components/manage/management-list/management-list.component.html - 101 + 102 src/app/components/manage/management-list/management-list.component.html - 101 + 102 src/app/components/manage/management-list/management-list.component.html - 101 + 102 src/app/components/manage/management-list/management-list.component.html - 101 + 102 @@ -2098,35 +2098,35 @@ src/app/components/manage/management-list/management-list.component.html - 83 + 84 src/app/components/manage/management-list/management-list.component.html - 83 + 84 src/app/components/manage/management-list/management-list.component.html - 83 + 84 src/app/components/manage/management-list/management-list.component.html - 83 + 84 src/app/components/manage/management-list/management-list.component.html - 95 + 96 src/app/components/manage/management-list/management-list.component.html - 95 + 96 src/app/components/manage/management-list/management-list.component.html - 95 + 96 src/app/components/manage/management-list/management-list.component.html - 95 + 96 src/app/components/manage/management-list/management-list.component.ts @@ -2405,35 +2405,35 @@ src/app/components/manage/management-list/management-list.component.html - 82 + 83 src/app/components/manage/management-list/management-list.component.html - 82 + 83 src/app/components/manage/management-list/management-list.component.html - 82 + 83 src/app/components/manage/management-list/management-list.component.html - 82 + 83 src/app/components/manage/management-list/management-list.component.html - 92 + 93 src/app/components/manage/management-list/management-list.component.html - 92 + 93 src/app/components/manage/management-list/management-list.component.html - 92 + 93 src/app/components/manage/management-list/management-list.component.html - 92 + 93 src/app/components/manage/workflows/workflows.component.html @@ -7986,19 +7986,19 @@ src/app/components/manage/management-list/management-list.component.html - 85 + 86 src/app/components/manage/management-list/management-list.component.html - 85 + 86 src/app/components/manage/management-list/management-list.component.html - 85 + 86 src/app/components/manage/management-list/management-list.component.html - 85 + 86 @@ -8382,19 +8382,19 @@ {VAR_PLURAL, plural, =1 {One } other { total }} src/app/components/manage/management-list/management-list.component.html - 116 + 118 src/app/components/manage/management-list/management-list.component.html - 116 + 118 src/app/components/manage/management-list/management-list.component.html - 116 + 118 src/app/components/manage/management-list/management-list.component.html - 116 + 118 diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.html b/src-ui/src/app/components/manage/management-list/management-list.component.html index ca4e93095..82fd8502c 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.html +++ b/src-ui/src/app/components/manage/management-list/management-list.component.html @@ -73,35 +73,37 @@ } -
-
- -
- - - @if (object.document_count > 0) { - - } +
+
+
+ +
+ + + @if (object.document_count > 0) { + + } +
-
-
- - -
- @if (object.document_count > 0) { -
- +
- } + @if (object.document_count > 0) { +
+ +
+ } +
} From 065724befb984795d86b22af97fdbdc8cb1d5c1b Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:57:57 -0800 Subject: [PATCH 25/37] Tweak: more verbose toast messages --- src-ui/messages.xlf | 294 +++++++++--------- .../components/admin/trash/trash.component.ts | 16 +- .../users-groups.component.spec.ts | 6 +- .../users-groups/users-groups.component.ts | 14 +- .../document-detail.component.spec.ts | 17 +- .../document-detail.component.ts | 21 +- .../custom-fields/custom-fields.component.ts | 7 +- .../manage/mail/mail.component.spec.ts | 10 +- .../components/manage/mail/mail.component.ts | 29 +- .../manage/workflows/workflows.component.ts | 18 +- 10 files changed, 248 insertions(+), 184 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 29852f89a..d3c93684b 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -256,7 +256,7 @@ src/app/components/admin/trash/trash.component.ts - 141 + 146 src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html @@ -2014,7 +2014,7 @@ src/app/components/admin/trash/trash.component.ts - 111 + 116 src/app/components/admin/users-groups/users-groups.component.html @@ -2160,7 +2160,7 @@ src/app/components/admin/trash/trash.component.ts - 105 + 110 src/app/components/manage/management-list/management-list.component.ts @@ -2186,7 +2186,7 @@ src/app/components/admin/trash/trash.component.ts - 109 + 114 src/app/components/admin/users-groups/users-groups.component.ts @@ -2194,7 +2194,7 @@ src/app/components/admin/users-groups/users-groups.component.ts - 174 + 177 src/app/components/manage/custom-fields/custom-fields.component.ts @@ -2206,7 +2206,7 @@ src/app/components/manage/mail/mail.component.ts - 286 + 296 src/app/components/manage/management-list/management-list.component.ts @@ -2217,78 +2217,74 @@ 137 - - Document deleted + + Document "" deleted src/app/components/admin/trash/trash.component.ts - 89 + 90 - - Error deleting document + + Error deleting document "" src/app/components/admin/trash/trash.component.ts - 94 - - - src/app/components/document-detail/document-detail.component.ts - 926 + 97 This operation will permanently delete the selected documents. src/app/components/admin/trash/trash.component.ts - 107 + 112 This operation will permanently delete all documents in the trash. src/app/components/admin/trash/trash.component.ts - 108 + 113 Document(s) deleted src/app/components/admin/trash/trash.component.ts - 119 + 124 Error deleting document(s) src/app/components/admin/trash/trash.component.ts - 126 + 131 - - Document restored + + Document "" restored src/app/components/admin/trash/trash.component.ts - 139 + 144 - - Error restoring document + + Error restoring document "" src/app/components/admin/trash/trash.component.ts - 149 + 155 Document(s) restored src/app/components/admin/trash/trash.component.ts - 159 + 167 Error restoring document(s) src/app/components/admin/trash/trash.component.ts - 165 + 173 @@ -2505,23 +2501,23 @@ src/app/components/admin/users-groups/users-groups.component.ts - 176 + 179 src/app/components/document-detail/document-detail.component.ts - 950 + 957 src/app/components/document-detail/document-detail.component.ts - 1303 + 1310 src/app/components/document-detail/document-detail.component.ts - 1342 + 1349 src/app/components/document-detail/document-detail.component.ts - 1383 + 1390 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -2545,7 +2541,7 @@ src/app/components/manage/mail/mail.component.ts - 288 + 298 src/app/components/manage/management-list/management-list.component.ts @@ -2556,60 +2552,60 @@ 139 - - Deleted user + + Deleted user "" src/app/components/admin/users-groups/users-groups.component.ts 132 - - Error deleting user. + + Error deleting user "". src/app/components/admin/users-groups/users-groups.component.ts - 138 + 139 Saved group "". src/app/components/admin/users-groups/users-groups.component.ts - 156 + 159 Error saving group. src/app/components/admin/users-groups/users-groups.component.ts - 164 + 167 Confirm delete user group src/app/components/admin/users-groups/users-groups.component.ts - 172 + 175 This operation will permanently delete this user group. src/app/components/admin/users-groups/users-groups.component.ts - 173 + 176 - - Deleted group + + Deleted group "" src/app/components/admin/users-groups/users-groups.component.ts - 182 + 185 - - Error deleting group. + + Error deleting group "". src/app/components/admin/users-groups/users-groups.component.ts - 188 + 192 @@ -3115,7 +3111,7 @@ src/app/components/document-detail/document-detail.component.ts - 903 + 910 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6241,7 +6237,7 @@ src/app/components/document-detail/document-detail.component.ts - 1360 + 1367 src/app/guards/dirty-saved-view.guard.ts @@ -6612,40 +6608,43 @@ 660 - - Document saved successfully. + + Document "" saved successfully. src/app/components/document-detail/document-detail.component.ts - 811 + 812 src/app/components/document-detail/document-detail.component.ts - 825 + 828 + + + + Error saving document "" + + src/app/components/document-detail/document-detail.component.ts + 834 Error saving document src/app/components/document-detail/document-detail.component.ts - 829 - - - src/app/components/document-detail/document-detail.component.ts - 872 + 879 Do you really want to move the document "" to the trash? src/app/components/document-detail/document-detail.component.ts - 904 + 911 Documents can be restored prior to permanent deletion. src/app/components/document-detail/document-detail.component.ts - 905 + 912 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6656,18 +6655,25 @@ Move to trash src/app/components/document-detail/document-detail.component.ts - 907 + 914 src/app/components/document-list/bulk-editor/bulk-editor.component.ts 751 + + Error deleting document + + src/app/components/document-detail/document-detail.component.ts + 933 + + Reprocess confirm src/app/components/document-detail/document-detail.component.ts - 946 + 953 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6678,77 +6684,77 @@ This operation will permanently recreate the archive file for this document. src/app/components/document-detail/document-detail.component.ts - 947 + 954 The archive file will be re-generated with the current settings. src/app/components/document-detail/document-detail.component.ts - 948 + 955 - - Reprocess operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content. + + Reprocess operation for "" will begin in the background. Close and re-open or reload this document after the operation has completed to see new content. src/app/components/document-detail/document-detail.component.ts - 958 + 965 Error executing operation src/app/components/document-detail/document-detail.component.ts - 969 + 976 Error downloading document src/app/components/document-detail/document-detail.component.ts - 1016 + 1023 Page Fit src/app/components/document-detail/document-detail.component.ts - 1088 + 1095 Split confirm src/app/components/document-detail/document-detail.component.ts - 1301 + 1308 This operation will split the selected document(s) into new documents. src/app/components/document-detail/document-detail.component.ts - 1302 + 1309 - - Split operation will begin in the background. + + Split operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1318 + 1325 Error executing split operation src/app/components/document-detail/document-detail.component.ts - 1327 + 1334 Rotate confirm src/app/components/document-detail/document-detail.component.ts - 1340 + 1347 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6759,60 +6765,60 @@ This operation will permanently rotate the original version of the current document. src/app/components/document-detail/document-detail.component.ts - 1341 + 1348 - - Rotation will begin in the background. Close and re-open the document after the operation has completed to see the changes. + + Rotation of "" will begin in the background. Close and re-open the document after the operation has completed to see the changes. src/app/components/document-detail/document-detail.component.ts - 1357 + 1364 Error executing rotate operation src/app/components/document-detail/document-detail.component.ts - 1369 + 1376 Delete pages confirm src/app/components/document-detail/document-detail.component.ts - 1381 + 1388 This operation will permanently delete the selected pages from the original document. src/app/components/document-detail/document-detail.component.ts - 1382 + 1389 - - Delete pages operation will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes. + + Delete pages operation for "" will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes. src/app/components/document-detail/document-detail.component.ts - 1397 + 1404 Error executing delete pages operation src/app/components/document-detail/document-detail.component.ts - 1406 + 1413 An error occurred loading tiff: src/app/components/document-detail/document-detail.component.ts - 1446 + 1453 src/app/components/document-detail/document-detail.component.ts - 1450 + 1457 @@ -8022,18 +8028,18 @@ 102 - - Deleted field + + Deleted field "" src/app/components/manage/custom-fields/custom-fields.component.ts 111 - - Error deleting field. + + Error deleting field "". src/app/components/manage/custom-fields/custom-fields.component.ts - 117 + 118 @@ -8212,109 +8218,109 @@ 194 - - Deleted mail account + + Deleted mail account "" src/app/components/manage/mail/mail.component.ts - 203 + 204 - - Error deleting mail account. + + Error deleting mail account "". src/app/components/manage/mail/mail.component.ts - 213 + 215 - - Processing mail account - - src/app/components/manage/mail/mail.component.ts - 224 - - - - Error processing mail account + + Processing mail account "" src/app/components/manage/mail/mail.component.ts 227 + + Error processing mail account "") + + src/app/components/manage/mail/mail.component.ts + 232 + + Saved rule "". src/app/components/manage/mail/mail.component.ts - 243 + 250 Error saving rule. src/app/components/manage/mail/mail.component.ts - 254 + 261 Rule "" enabled. src/app/components/manage/mail/mail.component.ts - 270 + 277 Rule "" disabled. src/app/components/manage/mail/mail.component.ts - 271 + 278 - - Error toggling rule. + + Error toggling rule "". src/app/components/manage/mail/mail.component.ts - 275 + 283 Confirm delete mail rule src/app/components/manage/mail/mail.component.ts - 284 + 294 This operation will permanently delete this mail rule. src/app/components/manage/mail/mail.component.ts - 285 + 295 - - Deleted mail rule + + Deleted mail rule "" src/app/components/manage/mail/mail.component.ts - 294 + 305 - - Error deleting mail rule. + + Error deleting mail rule "". src/app/components/manage/mail/mail.component.ts - 303 + 316 Permissions updated src/app/components/manage/mail/mail.component.ts - 325 + 340 Error updating permissions src/app/components/manage/mail/mail.component.ts - 330 + 345 src/app/components/manage/management-list/management-list.component.ts @@ -8650,39 +8656,39 @@ 136 - - Deleted workflow + + Deleted workflow "". src/app/components/manage/workflows/workflows.component.ts - 145 + 146 - - Error deleting workflow. + + Error deleting workflow "". src/app/components/manage/workflows/workflows.component.ts - 150 + 153 - - Enabled workflow + + Enabled workflow "" src/app/components/manage/workflows/workflows.component.ts - 161 + 166 - - Disabled workflow + + Disabled workflow "" src/app/components/manage/workflows/workflows.component.ts - 162 + 167 - - Error toggling workflow. + + Error toggling workflow "". src/app/components/manage/workflows/workflows.component.ts - 168 + 174 diff --git a/src-ui/src/app/components/admin/trash/trash.component.ts b/src-ui/src/app/components/admin/trash/trash.component.ts index fcf43e1c1..1df6ceff4 100644 --- a/src-ui/src/app/components/admin/trash/trash.component.ts +++ b/src-ui/src/app/components/admin/trash/trash.component.ts @@ -86,12 +86,17 @@ export class TrashComponent modal.componentInstance.buttonsEnabled = false this.trashService.emptyTrash([document.id]).subscribe({ next: () => { - this.toastService.showInfo($localize`Document deleted`) + this.toastService.showInfo( + $localize`Document "${document.title}" deleted` + ) modal.close() this.reload() }, error: (err) => { - this.toastService.showError($localize`Error deleting document`, err) + this.toastService.showError( + $localize`Error deleting document "${document.title}"`, + err + ) modal.close() }, }) @@ -136,7 +141,7 @@ export class TrashComponent this.trashService.restoreDocuments([document.id]).subscribe({ next: () => { this.toastService.show({ - content: $localize`Document restored`, + content: $localize`Document "${document.title}" restored`, delay: 5000, actionName: $localize`Open document`, action: () => { @@ -146,7 +151,10 @@ export class TrashComponent this.reload() }, error: (err) => { - this.toastService.showError($localize`Error restoring document`, err) + this.toastService.showError( + $localize`Error restoring document "${document.title}"`, + err + ) }, }) } diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts b/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts index ab739aa54..559b03f51 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts @@ -134,7 +134,7 @@ describe('UsersAndGroupsComponent', () => { deleteSpy.mockReturnValueOnce(of(true)) deleteDialog.confirm() expect(listAllSpy).toHaveBeenCalled() - expect(toastInfoSpy).toHaveBeenCalledWith('Deleted user') + expect(toastInfoSpy).toHaveBeenCalledWith('Deleted user "user1"') }) it('should logout current user if password changed, after delay', fakeAsync(() => { @@ -178,7 +178,7 @@ describe('UsersAndGroupsComponent', () => { completeSetup() let modal: NgbModalRef modalService.activeInstances.subscribe((refs) => (modal = refs[0])) - component.deleteGroup(users[0]) + component.deleteGroup(groups[0]) const deleteDialog = modal.componentInstance as ConfirmDialogComponent const deleteSpy = jest.spyOn(groupService, 'delete') const toastErrorSpy = jest.spyOn(toastService, 'showError') @@ -192,7 +192,7 @@ describe('UsersAndGroupsComponent', () => { deleteSpy.mockReturnValueOnce(of(true)) deleteDialog.confirm() expect(listAllSpy).toHaveBeenCalled() - expect(toastInfoSpy).toHaveBeenCalledWith('Deleted group') + expect(toastInfoSpy).toHaveBeenCalledWith('Deleted group "group1"') }) it('should get group name', () => { diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.ts b/src-ui/src/app/components/admin/users-groups/users-groups.component.ts index 41ed58dac..9ed73cde4 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.ts +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.ts @@ -129,13 +129,16 @@ export class UsersAndGroupsComponent this.usersService.delete(user).subscribe({ next: () => { modal.close() - this.toastService.showInfo($localize`Deleted user`) + this.toastService.showInfo($localize`Deleted user "${user.username}"`) this.usersService.listAll().subscribe((r) => { this.users = r.results }) }, error: (e) => { - this.toastService.showError($localize`Error deleting user.`, e) + this.toastService.showError( + $localize`Error deleting user "${user.username}".`, + e + ) }, }) }) @@ -179,13 +182,16 @@ export class UsersAndGroupsComponent this.groupsService.delete(group).subscribe({ next: () => { modal.close() - this.toastService.showInfo($localize`Deleted group`) + this.toastService.showInfo($localize`Deleted group "${group.name}"`) this.groupsService.listAll().subscribe((r) => { this.groups = r.results }) }, error: (e) => { - this.toastService.showError($localize`Error deleting group.`, e) + this.toastService.showError( + $localize`Error deleting group "${group.name}".`, + e + ) }, }) }) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index 8b2a84534..229c4fd12 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -454,7 +454,9 @@ describe('DocumentDetailComponent', () => { component.save(true) expect(updateSpy).toHaveBeenCalled() expect(closeSpy).toHaveBeenCalled() - expect(toastSpy).toHaveBeenCalledWith('Document saved successfully.') + expect(toastSpy).toHaveBeenCalledWith( + 'Document "Doc 3" saved successfully.' + ) }) it('should support save without close and show success toast', () => { @@ -467,7 +469,9 @@ describe('DocumentDetailComponent', () => { component.save() expect(updateSpy).toHaveBeenCalled() expect(closeSpy).not.toHaveBeenCalled() - expect(toastSpy).toHaveBeenCalledWith('Document saved successfully.') + expect(toastSpy).toHaveBeenCalledWith( + 'Document "Doc 3" saved successfully.' + ) }) it('should show toast error on save if error occurs', () => { @@ -482,7 +486,10 @@ describe('DocumentDetailComponent', () => { component.save() expect(updateSpy).toHaveBeenCalled() expect(closeSpy).not.toHaveBeenCalled() - expect(toastSpy).toHaveBeenCalledWith('Error saving document', error) + expect(toastSpy).toHaveBeenCalledWith( + 'Error saving document "Doc 3"', + error + ) }) it('should show error toast on save but close if user can no longer edit', () => { @@ -498,7 +505,9 @@ describe('DocumentDetailComponent', () => { component.save(true) expect(updateSpy).toHaveBeenCalled() expect(closeSpy).toHaveBeenCalled() - expect(toastSpy).toHaveBeenCalledWith('Document saved successfully.') + expect(toastSpy).toHaveBeenCalledWith( + 'Document "Doc 3" saved successfully.' + ) }) it('should allow save and next', () => { diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 0378fbb97..c1a96c168 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -808,7 +808,9 @@ export class DocumentDetailComponent this.store.next(newValues) this.openDocumentService.setDirty(this.document, false) this.openDocumentService.save() - this.toastService.showInfo($localize`Document saved successfully.`) + this.toastService.showInfo( + $localize`Document "${newValues.title}" saved successfully.` + ) this.networkActive = false this.error = null if (close) { @@ -822,11 +824,16 @@ export class DocumentDetailComponent error: (error) => { this.networkActive = false if (!this.userCanEdit) { - this.toastService.showInfo($localize`Document saved successfully.`) + this.toastService.showInfo( + $localize`Document "${this.document.title}" saved successfully.` + ) close && this.close() } else { this.error = error.error - this.toastService.showError($localize`Error saving document`, error) + this.toastService.showError( + $localize`Error saving document "${this.document.title}"`, + error + ) } }, }) @@ -955,7 +962,7 @@ export class DocumentDetailComponent .subscribe({ next: () => { this.toastService.showInfo( - $localize`Reprocess operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.` + $localize`Reprocess operation for "${this.document.title}" will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.` ) if (modal) { modal.close() @@ -1315,7 +1322,7 @@ export class DocumentDetailComponent .subscribe({ next: () => { this.toastService.showInfo( - $localize`Split operation will begin in the background.` + $localize`Split operation for "${this.document.title}" will begin in the background.` ) modal.close() }, @@ -1354,7 +1361,7 @@ export class DocumentDetailComponent .subscribe({ next: () => { this.toastService.show({ - content: $localize`Rotation will begin in the background. Close and re-open the document after the operation has completed to see the changes.`, + content: $localize`Rotation of "${this.document.title}" will begin in the background. Close and re-open the document after the operation has completed to see the changes.`, delay: 8000, action: this.close.bind(this), actionName: $localize`Close`, @@ -1394,7 +1401,7 @@ export class DocumentDetailComponent .subscribe({ next: () => { this.toastService.showInfo( - $localize`Delete pages operation will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes.` + $localize`Delete pages operation for "${this.document.title}" will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes.` ) modal.close() }, diff --git a/src-ui/src/app/components/manage/custom-fields/custom-fields.component.ts b/src-ui/src/app/components/manage/custom-fields/custom-fields.component.ts index 476b09106..9a17a4528 100644 --- a/src-ui/src/app/components/manage/custom-fields/custom-fields.component.ts +++ b/src-ui/src/app/components/manage/custom-fields/custom-fields.component.ts @@ -108,13 +108,16 @@ export class CustomFieldsComponent this.customFieldsService.delete(field).subscribe({ next: () => { modal.close() - this.toastService.showInfo($localize`Deleted field`) + this.toastService.showInfo($localize`Deleted field "${field.name}"`) this.customFieldsService.clearCache() this.settingsService.initializeDisplayFields() this.reload() }, error: (e) => { - this.toastService.showError($localize`Error deleting field.`, e) + this.toastService.showError( + $localize`Error deleting field "${field.name}".`, + e + ) }, }) }) diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index 3ece18061..b9f02343d 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -214,7 +214,7 @@ describe('MailComponent', () => { deleteSpy.mockReturnValueOnce(of(true)) deleteDialog.confirm() expect(listAllSpy).toHaveBeenCalled() - expect(toastInfoSpy).toHaveBeenCalledWith('Deleted mail account') + expect(toastInfoSpy).toHaveBeenCalledWith('Deleted mail account "account1"') }) it('should support process mail account, show error if needed', () => { @@ -231,7 +231,9 @@ describe('MailComponent', () => { expect(toastErrorSpy).toHaveBeenCalled() processSpy.mockReturnValueOnce(of(true)) component.processAccount(mailAccounts[0] as MailAccount) - expect(toastInfoSpy).toHaveBeenCalledWith('Processing mail account') + expect(toastInfoSpy).toHaveBeenCalledWith( + 'Processing mail account "account1"' + ) }) it('should support edit / create mail rule, show error if needed', () => { @@ -274,14 +276,14 @@ describe('MailComponent', () => { const toastInfoSpy = jest.spyOn(toastService, 'showInfo') const listAllSpy = jest.spyOn(mailRuleService, 'listAll') deleteSpy.mockReturnValueOnce( - throwError(() => new Error('error deleting mail rule')) + throwError(() => new Error('error deleting mail rule "rule1"')) ) deleteDialog.confirm() expect(toastErrorSpy).toBeCalled() deleteSpy.mockReturnValueOnce(of(true)) deleteDialog.confirm() expect(listAllSpy).toHaveBeenCalled() - expect(toastInfoSpy).toHaveBeenCalledWith('Deleted mail rule') + expect(toastInfoSpy).toHaveBeenCalledWith('Deleted mail rule "rule1"') }) it('should support edit permissions on mail rule objects', () => { diff --git a/src-ui/src/app/components/manage/mail/mail.component.ts b/src-ui/src/app/components/manage/mail/mail.component.ts index b606a33c9..c97d7e893 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -200,7 +200,9 @@ export class MailComponent this.mailAccountService.delete(account).subscribe({ next: () => { modal.close() - this.toastService.showInfo($localize`Deleted mail account`) + this.toastService.showInfo( + $localize`Deleted mail account "${account.name}"` + ) this.mailAccountService.clearCache() this.mailAccountService .listAll(null, null, { full_perms: true }) @@ -210,7 +212,7 @@ export class MailComponent }, error: (e) => { this.toastService.showError( - $localize`Error deleting mail account.`, + $localize`Error deleting mail account "${account.name}".`, e ) }, @@ -221,10 +223,15 @@ export class MailComponent processAccount(account: MailAccount) { this.mailAccountService.processAccount(account).subscribe({ next: () => { - this.toastService.showInfo($localize`Processing mail account`) + this.toastService.showInfo( + $localize`Processing mail account "${account.name}"` + ) }, error: (e) => { - this.toastService.showError($localize`Error processing mail account`, e) + this.toastService.showError( + $localize`Error processing mail account "${account.name}")`, + e + ) }, }) } @@ -272,7 +279,10 @@ export class MailComponent ) }, error: (e) => { - this.toastService.showError($localize`Error toggling rule.`, e) + this.toastService.showError( + $localize`Error toggling rule "${rule.name}".`, + e + ) }, }) } @@ -291,7 +301,9 @@ export class MailComponent this.mailRuleService.delete(rule).subscribe({ next: () => { modal.close() - this.toastService.showInfo($localize`Deleted mail rule`) + this.toastService.showInfo( + $localize`Deleted mail rule "${rule.name}"` + ) this.mailRuleService.clearCache() this.mailRuleService .listAll(null, null, { full_perms: true }) @@ -300,7 +312,10 @@ export class MailComponent }) }, error: (e) => { - this.toastService.showError($localize`Error deleting mail rule.`, e) + this.toastService.showError( + $localize`Error deleting mail rule "${rule.name}".`, + e + ) }, }) }) diff --git a/src-ui/src/app/components/manage/workflows/workflows.component.ts b/src-ui/src/app/components/manage/workflows/workflows.component.ts index b1f9ff6d0..edbca44c8 100644 --- a/src-ui/src/app/components/manage/workflows/workflows.component.ts +++ b/src-ui/src/app/components/manage/workflows/workflows.component.ts @@ -142,12 +142,17 @@ export class WorkflowsComponent this.workflowService.delete(workflow).subscribe({ next: () => { modal.close() - this.toastService.showInfo($localize`Deleted workflow`) + this.toastService.showInfo( + $localize`Deleted workflow "${workflow.name}".` + ) this.workflowService.clearCache() this.reload() }, error: (e) => { - this.toastService.showError($localize`Error deleting workflow.`, e) + this.toastService.showError( + $localize`Error deleting workflow "${workflow.name}".`, + e + ) }, }) }) @@ -158,14 +163,17 @@ export class WorkflowsComponent next: () => { this.toastService.showInfo( workflow.enabled - ? $localize`Enabled workflow` - : $localize`Disabled workflow` + ? $localize`Enabled workflow "${workflow.name}"` + : $localize`Disabled workflow "${workflow.name}"` ) this.workflowService.clearCache() this.reload() }, error: (e) => { - this.toastService.showError($localize`Error toggling workflow.`, e) + this.toastService.showError( + $localize`Error toggling workflow "${workflow.name}".`, + e + ) }, }) } From 6f29d6432559e282fe1b067c246855e31ec81135 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:59:45 -0800 Subject: [PATCH 26/37] Fix: allow empty email in profile (#9012) --- src/paperless/serialisers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/paperless/serialisers.py b/src/paperless/serialisers.py index fb1f511f7..cd9325c09 100644 --- a/src/paperless/serialisers.py +++ b/src/paperless/serialisers.py @@ -162,7 +162,7 @@ class SocialAccountSerializer(serializers.ModelSerializer): class ProfileSerializer(serializers.ModelSerializer): - email = serializers.EmailField(allow_null=False) + email = serializers.EmailField(allow_blank=True, required=False) password = ObfuscatedUserPasswordField(required=False, allow_null=False) auth_token = serializers.SlugRelatedField(read_only=True, slug_field="key") social_accounts = SocialAccountSerializer( From 49390c9427ff8cee9926522619544394aca859e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 18:00:47 -0800 Subject: [PATCH 27/37] Chore(deps): Bump django-soft-delete in the django group (#9014) Bumps the django group with 1 update: [django-soft-delete](https://github.com/san4ezy/django_softdelete). Updates `django-soft-delete` from 1.0.16 to 1.0.18 - [Changelog](https://github.com/san4ezy/django_softdelete/blob/master/CHANGELOG.md) - [Commits](https://github.com/san4ezy/django_softdelete/commits) --- updated-dependencies: - dependency-name: django-soft-delete dependency-type: direct:production update-type: version-update:semver-patch dependency-group: django ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Pipfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 1889d3d4a..d7294f472 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3806c1dbfde8e9383e748c106c217170d6dcdbb8b95d573030b2294dab32d462" + "sha256": "6a7869231917d0cf6f5852520b5cb9b0df3802ed162b1a8107d0b1e1c37f0535" }, "pipfile-spec": 6, "requires": {}, @@ -589,12 +589,12 @@ }, "django-soft-delete": { "hashes": [ - "sha256:cc40398ccd869c75a6d6ba7f526e16c4afe2b0c0811c213a318d96bb4c58a787", - "sha256:fdaf2788d404930557f1300ce40bbd764f6938775a35a3175c66fe7778666093" + "sha256:603a29e82bbb7a5bada69f2754fad225ccd8cd7f485320ec06d0fc4e9dfddcf0", + "sha256:d2f9db449a4f008e9786f82fa4bafbe4075f7a0b3284844735007e988b2a4df6" ], "index": "pypi", "markers": "python_version >= '3.6'", - "version": "==1.0.16" + "version": "==1.0.18" }, "djangorestframework": { "hashes": [ @@ -2264,7 +2264,7 @@ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version >= '3.8'", + "markers": "python_version < '3.11'", "version": "==4.12.2" }, "tzdata": { From 7ab779e78af05decdaa1ba4938fed9f632965b15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 03:21:55 +0000 Subject: [PATCH 28/37] Chore(deps-dev): Bump the development group with 2 updates (#9013) * Chore(deps-dev): Bump the development group with 2 updates Bumps the development group with 2 updates: [ruff](https://github.com/astral-sh/ruff) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material). Updates `ruff` from 0.9.3 to 0.9.4 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.9.3...0.9.4) Updates `mkdocs-material` from 9.5.50 to 9.6.2 - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.50...9.6.2) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development - dependency-name: mkdocs-material dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development ... Signed-off-by: dependabot[bot] * Update .pre-commit-config.yaml --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- Pipfile.lock | 69 ++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 37 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2ddffaf9f..10f9d0084 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,7 @@ repos: - 'prettier-plugin-organize-imports@4.1.0' # Python hooks - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.3 + rev: v0.9.4 hooks: - id: ruff - id: ruff-format diff --git a/Pipfile.lock b/Pipfile.lock index d7294f472..1f74e6708 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -2837,19 +2837,19 @@ }, "babel": { "hashes": [ - "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", - "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316" + "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", + "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2" ], "markers": "python_version >= '3.8'", - "version": "==2.16.0" + "version": "==2.17.0" }, "certifi": { "hashes": [ - "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", - "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" + "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", + "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" ], "markers": "python_version >= '3.6'", - "version": "==2024.12.14" + "version": "==2025.1.31" }, "cffi": { "hashes": [ @@ -3302,7 +3302,6 @@ "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb" ], - "index": "pypi", "markers": "python_version >= '3.7'", "version": "==3.1.5" }, @@ -3415,12 +3414,12 @@ }, "mkdocs-material": { "hashes": [ - "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825", - "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385" + "sha256:71d90dbd63b393ad11a4d90151dfe3dcbfcd802c0f29ce80bebd9bbac6abc753", + "sha256:a3de1c5d4c745f10afa78b1a02f917b9dce0808fb206adc0f5bb48b58c1ca21f" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==9.5.50" + "version": "==9.6.2" }, "mkdocs-material-extensions": { "hashes": [ @@ -3658,11 +3657,11 @@ }, "pymdown-extensions": { "hashes": [ - "sha256:637951cbfbe9874ba28134fb3ce4b8bcadd6aca89ac4998ec29dcbafd554ae08", - "sha256:b65801996a0cd4f42a3110810c306c45b7313c09b0610a6f773730f2a9e3c96b" + "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", + "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b" ], "markers": "python_version >= '3.8'", - "version": "==10.14.1" + "version": "==10.14.3" }, "pyopenssl": { "hashes": [ @@ -3757,8 +3756,7 @@ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.9.0.post0" }, "pywavelets": { @@ -3982,28 +3980,28 @@ }, "ruff": { "hashes": [ - "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2", - "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4", - "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439", - "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730", - "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4", - "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5", - "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624", - "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b", - "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a", - "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b", - "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5", - "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4", - "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c", - "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519", - "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1", - "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4", - "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6", - "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c" + "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e", + "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214", + "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137", + "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c", + "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b", + "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b", + "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41", + "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706", + "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7", + "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf", + "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec", + "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6", + "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231", + "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0", + "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402", + "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e", + "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a", + "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.9.3" + "version": "==0.9.4" }, "scipy": { "hashes": [ @@ -4072,7 +4070,7 @@ "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.17.0" }, "sniffio": { @@ -4205,7 +4203,6 @@ "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2" ], - "index": "pypi", "markers": "python_version >= '3.9'", "version": "==6.0.0" }, From 2103a499eba83addeb0a53d0ea4f6ce774ed7e35 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:54:18 -0800 Subject: [PATCH 29/37] Enhancement: allow setting default pdf zoom (#9017) --- src-ui/messages.xlf | 404 ++++++++++-------- .../admin/settings/settings.component.html | 95 ++-- .../admin/settings/settings.component.spec.ts | 2 +- .../admin/settings/settings.component.ts | 11 + .../document-detail.component.html | 8 +- .../document-detail.component.scss | 3 + .../document-detail.component.spec.ts | 26 +- .../document-detail.component.ts | 20 +- src-ui/src/app/data/ui-settings.ts | 7 + 9 files changed, 332 insertions(+), 244 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index d3c93684b..65e25d8ba 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -545,7 +545,7 @@ src/app/components/admin/settings/settings.component.html - 349 + 364 src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html @@ -944,60 +944,25 @@ 152 - - Document editing - - src/app/components/admin/settings/settings.component.html - 157 - - - - Use PDF viewer provided by the browser - - src/app/components/admin/settings/settings.component.html - 161 - - - - This is usually faster for displaying large PDF documents, but it might not work on some browsers. - - src/app/components/admin/settings/settings.component.html - 161 - - - - Automatically remove inbox tag(s) on save - - src/app/components/admin/settings/settings.component.html - 167 - - - - Show document thumbnail during loading - - src/app/components/admin/settings/settings.component.html - 173 - - Update checking src/app/components/admin/settings/settings.component.html - 178 + 157 Enable update checking src/app/components/admin/settings/settings.component.html - 181 + 160 What's this? src/app/components/admin/settings/settings.component.html - 182 + 161 src/app/components/common/page-header/page-header.component.html @@ -1012,89 +977,21 @@ Update checking works by pinging the public GitHub API for the latest release to determine whether a new version is available. Actual updating of the app must still be performed manually. src/app/components/admin/settings/settings.component.html - 186,188 + 165,167 No tracking data is collected by the app in any way. src/app/components/admin/settings/settings.component.html - 190 - - - - Bulk editing - - src/app/components/admin/settings/settings.component.html - 196 - - - - Show confirmation dialogs - - src/app/components/admin/settings/settings.component.html - 199 - - - - Apply on close - - src/app/components/admin/settings/settings.component.html - 200 - - - - Global search - - src/app/components/admin/settings/settings.component.html - 204 - - - src/app/components/app-frame/global-search/global-search.component.ts - 120 - - - - Do not include advanced search results - - src/app/components/admin/settings/settings.component.html - 207 - - - - Full search links to - - src/app/components/admin/settings/settings.component.html - 215 - - - - Title and content search - - src/app/components/admin/settings/settings.component.html - 219 - - - - Advanced search - - src/app/components/admin/settings/settings.component.html - 220 - - - src/app/components/app-frame/global-search/global-search.component.html - 24 - - - src/app/components/document-list/filter-editor/filter-editor.component.ts - 166 + 169 Saved Views src/app/components/admin/settings/settings.component.html - 227 + 175 src/app/components/app-frame/app-frame.component.html @@ -1113,14 +1010,77 @@ Show warning when closing saved views with unsaved changes src/app/components/admin/settings/settings.component.html - 230 + 178 + + + + Document editing + + src/app/components/admin/settings/settings.component.html + 184 + + + + Use PDF viewer provided by the browser + + src/app/components/admin/settings/settings.component.html + 188 + + + + This is usually faster for displaying large PDF documents, but it might not work on some browsers. + + src/app/components/admin/settings/settings.component.html + 188 + + + + Default zoom: + + src/app/components/admin/settings/settings.component.html + 194 + + + + Fit width + + src/app/components/admin/settings/settings.component.html + 198 + + + + Fit page + + src/app/components/admin/settings/settings.component.html + 199 + + + + Only applies to the Paperless-ngx PDF viewer. + + src/app/components/admin/settings/settings.component.html + 201 + + + + Automatically remove inbox tag(s) on save + + src/app/components/admin/settings/settings.component.html + 207 + + + + Show document thumbnail during loading + + src/app/components/admin/settings/settings.component.html + 213 Notes src/app/components/admin/settings/settings.component.html - 234 + 217 src/app/components/document-list/document-list.component.html @@ -1139,14 +1099,82 @@ Enable notes src/app/components/admin/settings/settings.component.html - 237 + 220 + + + + Bulk editing + + src/app/components/admin/settings/settings.component.html + 224 + + + + Show confirmation dialogs + + src/app/components/admin/settings/settings.component.html + 227 + + + + Apply on close + + src/app/components/admin/settings/settings.component.html + 228 + + + + Global search + + src/app/components/admin/settings/settings.component.html + 232 + + + src/app/components/app-frame/global-search/global-search.component.ts + 120 + + + + Do not include advanced search results + + src/app/components/admin/settings/settings.component.html + 235 + + + + Full search links to + + src/app/components/admin/settings/settings.component.html + 243 + + + + Title and content search + + src/app/components/admin/settings/settings.component.html + 247 + + + + Advanced search + + src/app/components/admin/settings/settings.component.html + 248 + + + src/app/components/app-frame/global-search/global-search.component.html + 24 + + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 166 Permissions src/app/components/admin/settings/settings.component.html - 247 + 262 src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html @@ -1209,28 +1237,28 @@ Default Permissions src/app/components/admin/settings/settings.component.html - 250 + 265 Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI src/app/components/admin/settings/settings.component.html - 254,256 + 269,271 Default Owner src/app/components/admin/settings/settings.component.html - 261 + 276 Objects without an owner can be viewed and edited by all users src/app/components/admin/settings/settings.component.html - 265 + 280 src/app/components/common/input/permissions/permissions-form/permissions-form.component.html @@ -1241,18 +1269,18 @@ Default View Permissions src/app/components/admin/settings/settings.component.html - 270 + 285 Users: src/app/components/admin/settings/settings.component.html - 275 + 290 src/app/components/admin/settings/settings.component.html - 302 + 317 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1283,11 +1311,11 @@ Groups: src/app/components/admin/settings/settings.component.html - 285 + 300 src/app/components/admin/settings/settings.component.html - 312 + 327 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1318,14 +1346,14 @@ Default Edit Permissions src/app/components/admin/settings/settings.component.html - 297 + 312 Edit permissions also grant viewing permissions src/app/components/admin/settings/settings.component.html - 321 + 336 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1344,56 +1372,56 @@ Notifications src/app/components/admin/settings/settings.component.html - 329 + 344 Document processing src/app/components/admin/settings/settings.component.html - 332 + 347 Show notifications when new documents are detected src/app/components/admin/settings/settings.component.html - 336 + 351 Show notifications when document processing completes successfully src/app/components/admin/settings/settings.component.html - 337 + 352 Show notifications when document processing fails src/app/components/admin/settings/settings.component.html - 338 + 353 Suppress notifications on dashboard src/app/components/admin/settings/settings.component.html - 339 + 354 This will suppress all messages about document processing status on the dashboard. src/app/components/admin/settings/settings.component.html - 339 + 354 Cancel src/app/components/admin/settings/settings.component.html - 350 + 365 src/app/components/common/confirm-dialog/confirm-dialog.component.ts @@ -1468,21 +1496,21 @@ Use system language src/app/components/admin/settings/settings.component.ts - 75 + 76 Use date format of display language src/app/components/admin/settings/settings.component.ts - 78 + 79 Error retrieving users src/app/components/admin/settings/settings.component.ts - 213 + 217 src/app/components/admin/users-groups/users-groups.component.ts @@ -1493,7 +1521,7 @@ Error retrieving groups src/app/components/admin/settings/settings.component.ts - 232 + 236 src/app/components/admin/users-groups/users-groups.component.ts @@ -1504,28 +1532,28 @@ Settings were saved successfully. src/app/components/admin/settings/settings.component.ts - 521 + 532 Settings were saved successfully. Reload is required to apply some changes. src/app/components/admin/settings/settings.component.ts - 525 + 536 Reload now src/app/components/admin/settings/settings.component.ts - 526 + 537 An error occurred while saving settings. src/app/components/admin/settings/settings.component.ts - 536 + 547 src/app/components/app-frame/app-frame.component.ts @@ -2505,19 +2533,19 @@ src/app/components/document-detail/document-detail.component.ts - 957 + 958 src/app/components/document-detail/document-detail.component.ts - 1310 + 1318 src/app/components/document-detail/document-detail.component.ts - 1349 + 1357 src/app/components/document-detail/document-detail.component.ts - 1390 + 1398 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -3111,7 +3139,7 @@ src/app/components/document-detail/document-detail.component.ts - 910 + 911 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6237,7 +6265,7 @@ src/app/components/document-detail/document-detail.component.ts - 1367 + 1375 src/app/guards/dirty-saved-view.guard.ts @@ -6524,56 +6552,56 @@ An error occurred loading content: src/app/components/document-detail/document-detail.component.ts - 411,413 + 412,414 Document changes detected src/app/components/document-detail/document-detail.component.ts - 434 + 435 The version of this document in your browser session appears older than the existing version. src/app/components/document-detail/document-detail.component.ts - 435 + 436 Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document. src/app/components/document-detail/document-detail.component.ts - 436 + 437 Ok src/app/components/document-detail/document-detail.component.ts - 438 + 439 Next document src/app/components/document-detail/document-detail.component.ts - 545 + 546 Previous document src/app/components/document-detail/document-detail.component.ts - 555 + 556 Close document src/app/components/document-detail/document-detail.component.ts - 563 + 564 src/app/services/open-documents.service.ts @@ -6584,67 +6612,67 @@ Save document src/app/components/document-detail/document-detail.component.ts - 570 + 571 Save and close / next src/app/components/document-detail/document-detail.component.ts - 579 + 580 Error retrieving metadata src/app/components/document-detail/document-detail.component.ts - 631 + 632 Error retrieving suggestions. src/app/components/document-detail/document-detail.component.ts - 660 + 661 Document "" saved successfully. src/app/components/document-detail/document-detail.component.ts - 812 + 813 src/app/components/document-detail/document-detail.component.ts - 828 + 829 Error saving document "" src/app/components/document-detail/document-detail.component.ts - 834 + 835 Error saving document src/app/components/document-detail/document-detail.component.ts - 879 + 880 Do you really want to move the document "" to the trash? src/app/components/document-detail/document-detail.component.ts - 911 + 912 Documents can be restored prior to permanent deletion. src/app/components/document-detail/document-detail.component.ts - 912 + 913 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6655,7 +6683,7 @@ Move to trash src/app/components/document-detail/document-detail.component.ts - 914 + 915 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6666,14 +6694,14 @@ Error deleting document src/app/components/document-detail/document-detail.component.ts - 933 + 934 Reprocess confirm src/app/components/document-detail/document-detail.component.ts - 953 + 954 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6684,77 +6712,77 @@ This operation will permanently recreate the archive file for this document. src/app/components/document-detail/document-detail.component.ts - 954 + 955 The archive file will be re-generated with the current settings. src/app/components/document-detail/document-detail.component.ts - 955 + 956 Reprocess operation for "" will begin in the background. Close and re-open or reload this document after the operation has completed to see new content. src/app/components/document-detail/document-detail.component.ts - 965 + 966 Error executing operation src/app/components/document-detail/document-detail.component.ts - 976 + 977 Error downloading document src/app/components/document-detail/document-detail.component.ts - 1023 + 1024 Page Fit src/app/components/document-detail/document-detail.component.ts - 1095 + 1103 Split confirm src/app/components/document-detail/document-detail.component.ts - 1308 + 1316 This operation will split the selected document(s) into new documents. src/app/components/document-detail/document-detail.component.ts - 1309 + 1317 Split operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1325 + 1333 Error executing split operation src/app/components/document-detail/document-detail.component.ts - 1334 + 1342 Rotate confirm src/app/components/document-detail/document-detail.component.ts - 1347 + 1355 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -6765,60 +6793,60 @@ This operation will permanently rotate the original version of the current document. src/app/components/document-detail/document-detail.component.ts - 1348 + 1356 Rotation of "" will begin in the background. Close and re-open the document after the operation has completed to see the changes. src/app/components/document-detail/document-detail.component.ts - 1364 + 1372 Error executing rotate operation src/app/components/document-detail/document-detail.component.ts - 1376 + 1384 Delete pages confirm src/app/components/document-detail/document-detail.component.ts - 1388 + 1396 This operation will permanently delete the selected pages from the original document. src/app/components/document-detail/document-detail.component.ts - 1389 + 1397 Delete pages operation for "" will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes. src/app/components/document-detail/document-detail.component.ts - 1404 + 1412 Error executing delete pages operation src/app/components/document-detail/document-detail.component.ts - 1413 + 1421 An error occurred loading tiff: src/app/components/document-detail/document-detail.component.ts - 1453 + 1461 src/app/components/document-detail/document-detail.component.ts - 1457 + 1465 diff --git a/src-ui/src/app/components/admin/settings/settings.component.html b/src-ui/src/app/components/admin/settings/settings.component.html index 097015973..b8a46e57e 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.html +++ b/src-ui/src/app/components/admin/settings/settings.component.html @@ -41,7 +41,7 @@
-

Appearance

+
Appearance
Display language @@ -154,28 +154,7 @@
-

Document editing

- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
-

Update checking

+
Update checking
@@ -193,7 +172,56 @@
-

Bulk editing

+
Saved Views
+
+
+ +
+
+ +
+
+
Document editing
+ +
+
+ +
+
+ +
+
+ Default zoom: +
+
+ +

Only applies to the Paperless-ngx PDF viewer.

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
Notes
+
+
+ +
+
+ +
Bulk editing
@@ -201,7 +229,7 @@
-

Global search

+
Global search
@@ -224,19 +252,6 @@
-

Saved Views

-
-
- -
-
- -

Notes

-
-
- -
-
@@ -247,7 +262,7 @@ Permissions -

Default Permissions

+
Default Permissions
@@ -329,7 +344,7 @@ Notifications -

Document processing

+
Document processing
diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 5f587cf9e..4f50e7453 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -212,7 +212,7 @@ describe('SettingsComponent', () => { expect(toastErrorSpy).toHaveBeenCalled() expect(storeSpy).toHaveBeenCalled() expect(appearanceSettingsSpy).not.toHaveBeenCalled() - expect(setSpy).toHaveBeenCalledTimes(28) + expect(setSpy).toHaveBeenCalledTimes(29) // succeed storeSpy.mockReturnValueOnce(of(true)) diff --git a/src-ui/src/app/components/admin/settings/settings.component.ts b/src-ui/src/app/components/admin/settings/settings.component.ts index 9bd044f78..68f702cfa 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.ts @@ -63,6 +63,7 @@ import { PermissionsUserComponent } from '../../common/input/permissions/permiss import { SelectComponent } from '../../common/input/select/select.component' import { PageHeaderComponent } from '../../common/page-header/page-header.component' import { SystemStatusDialogComponent } from '../../common/system-status-dialog/system-status-dialog.component' +import { ZoomSetting } from '../../document-detail/document-detail.component' import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' enum SettingsNavIDs { @@ -125,6 +126,7 @@ export class SettingsComponent defaultPermsEditUsers: new FormControl(null), defaultPermsEditGroups: new FormControl(null), useNativePdfViewer: new FormControl(null), + pdfViewerDefaultZoom: new FormControl(null), documentEditingRemoveInboxTags: new FormControl(null), documentEditingOverlayThumbnail: new FormControl(null), searchDbOnly: new FormControl(null), @@ -154,6 +156,8 @@ export class SettingsComponent public readonly GlobalSearchType = GlobalSearchType + public readonly ZoomSetting = ZoomSetting + get systemStatusHasErrors(): boolean { return ( this.systemStatus.database.status === SystemStatusItemStatus.ERROR || @@ -276,6 +280,9 @@ export class SettingsComponent useNativePdfViewer: this.settings.get( SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER ), + pdfViewerDefaultZoom: this.settings.get( + SETTINGS_KEYS.PDF_VIEWER_ZOOM_SETTING + ), displayLanguage: this.settings.getLanguage(), dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE), dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT), @@ -435,6 +442,10 @@ export class SettingsComponent SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer ) + this.settings.set( + SETTINGS_KEYS.PDF_VIEWER_ZOOM_SETTING, + this.settingsForm.value.pdfViewerDefaultZoom + ) this.settings.set( SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index 02fec3cf7..a8e14c51d 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -9,9 +9,9 @@ }
- @for (setting of zoomSettings; track setting) { - } @@ -356,9 +356,9 @@ -
+
@if (showThumbnailOverlay) { - Document loading... + Document loading... }
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.scss b/src-ui/src/app/components/document-detail/document-detail.component.scss index c00f7655e..3fc009020 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.scss +++ b/src-ui/src/app/components/document-detail/document-detail.component.scss @@ -85,5 +85,8 @@ textarea.rtl { > img { filter: blur(1px); + max-width: 100%; + object-fit: contain; + object-position: top; } } diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index 229c4fd12..349e213aa 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -62,7 +62,10 @@ import { ToastService } from 'src/app/services/toast.service' import { environment } from 'src/environments/environment' import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component' import { CustomFieldsDropdownComponent } from '../common/custom-fields-dropdown/custom-fields-dropdown.component' -import { DocumentDetailComponent } from './document-detail.component' +import { + DocumentDetailComponent, + ZoomSetting, +} from './document-detail.component' const doc: Document = { id: 3, @@ -753,7 +756,7 @@ describe('DocumentDetailComponent', () => { it('should support zoom controls', () => { initNormally() - component.onZoomSelect({ target: { value: '1' } } as any) // from select + component.setZoom(ZoomSetting.One) // from select expect(component.previewZoomSetting).toEqual('1') component.increaseZoom() expect(component.previewZoomSetting).toEqual('1.5') @@ -761,18 +764,18 @@ describe('DocumentDetailComponent', () => { expect(component.previewZoomSetting).toEqual('2') component.decreaseZoom() expect(component.previewZoomSetting).toEqual('1.5') - component.onZoomSelect({ target: { value: '1' } } as any) // from select + component.setZoom(ZoomSetting.One) // from select component.decreaseZoom() expect(component.previewZoomSetting).toEqual('.75') - component.onZoomSelect({ target: { value: 'page-fit' } } as any) // from select + component.setZoom(ZoomSetting.PageFit) // from select expect(component.previewZoomScale).toEqual('page-fit') expect(component.previewZoomSetting).toEqual('1') component.increaseZoom() expect(component.previewZoomSetting).toEqual('1.5') expect(component.previewZoomScale).toEqual('page-width') - component.onZoomSelect({ target: { value: 'page-fit' } } as any) // from select + component.setZoom(ZoomSetting.PageFit) // from select expect(component.previewZoomScale).toEqual('page-fit') expect(component.previewZoomSetting).toEqual('1') component.decreaseZoom() @@ -780,6 +783,19 @@ describe('DocumentDetailComponent', () => { expect(component.previewZoomScale).toEqual('page-width') }) + it('should select correct zoom setting in dropdown', () => { + initNormally() + component.setZoom(ZoomSetting.PageFit) + expect(component.isZoomSelected(ZoomSetting.PageFit)).toBeTruthy() + expect(component.isZoomSelected(ZoomSetting.One)).toBeFalsy() + component.setZoom(ZoomSetting.PageWidth) + expect(component.isZoomSelected(ZoomSetting.One)).toBeTruthy() + expect(component.isZoomSelected(ZoomSetting.PageFit)).toBeFalsy() + component.setZoom(ZoomSetting.Quarter) + expect(component.isZoomSelected(ZoomSetting.Quarter)).toBeTruthy() + expect(component.isZoomSelected(ZoomSetting.PageFit)).toBeFalsy() + }) + it('should support updating notes dynamically', () => { const notes = [ { diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index c1a96c168..30e34d9cf 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -124,7 +124,7 @@ enum ContentRenderType { TIFF = 'tiff', } -enum ZoomSetting { +export enum ZoomSetting { PageFit = 'page-fit', PageWidth = 'page-width', Quarter = '.25', @@ -328,6 +328,7 @@ export class DocumentDetailComponent } ngOnInit(): void { + this.setZoom(this.settings.get(SETTINGS_KEYS.PDF_VIEWER_ZOOM_SETTING)) this.documentForm.valueChanges .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { @@ -1072,14 +1073,13 @@ export class DocumentDetailComponent } } - onZoomSelect(event: Event) { - const setting = (event.target as HTMLSelectElement)?.value as ZoomSetting - if (ZoomSetting.PageFit === setting) { - this.previewZoomSetting = ZoomSetting.One + setZoom(setting: ZoomSetting) { + if (ZoomSetting.PageFit === setting || ZoomSetting.PageWidth === setting) { this.previewZoomScale = setting + this.previewZoomSetting = ZoomSetting.One } else { - this.previewZoomScale = ZoomSetting.PageWidth this.previewZoomSetting = setting + this.previewZoomScale = ZoomSetting.PageWidth } } @@ -1089,6 +1089,14 @@ export class DocumentDetailComponent ) } + isZoomSelected(setting: ZoomSetting): boolean { + if (this.previewZoomScale === ZoomSetting.PageFit) { + return setting === ZoomSetting.PageFit + } + + return this.previewZoomSetting === setting + } + getZoomSettingTitle(setting: ZoomSetting): string { switch (setting) { case ZoomSetting.PageFit: diff --git a/src-ui/src/app/data/ui-settings.ts b/src-ui/src/app/data/ui-settings.ts index b8a319d9b..c5164d6e1 100644 --- a/src-ui/src/app/data/ui-settings.ts +++ b/src-ui/src/app/data/ui-settings.ts @@ -33,6 +33,8 @@ export const SETTINGS_KEYS = { DARK_MODE_THUMB_INVERTED: 'general-settings:dark-mode:thumb-inverted', THEME_COLOR: 'general-settings:theme:color', USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer', + PDF_VIEWER_ZOOM_SETTING: + 'general-settings:document-details:pdf-viewer-zoom-setting', DATE_LOCALE: 'general-settings:date-display:date-locale', DATE_FORMAT: 'general-settings:date-display:date-format', NOTIFICATIONS_CONSUMER_NEW_DOCUMENT: @@ -269,4 +271,9 @@ export const SETTINGS: UiSetting[] = [ type: 'boolean', default: false, }, + { + key: SETTINGS_KEYS.PDF_VIEWER_ZOOM_SETTING, + type: 'string', + default: 'page-width', // ZoomSetting from 'document-detail.component' + }, ] From 3314c5982859609eea1635bfdb8545b7df1a7c07 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:54:31 -0800 Subject: [PATCH 30/37] Tweak: more accurate classifier last trained time (#9004) --- src/documents/classifier.py | 16 ++++++++++++++++ src/documents/views.py | 17 ++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/documents/classifier.py b/src/documents/classifier.py index 72bf1f16c..5bc8be2c6 100644 --- a/src/documents/classifier.py +++ b/src/documents/classifier.py @@ -1,6 +1,7 @@ import logging import pickle import re +import time import warnings from collections.abc import Iterator from hashlib import sha256 @@ -141,6 +142,19 @@ class DocumentClassifier: ): raise IncompatibleClassifierVersionError("sklearn version update") + def set_last_checked(self) -> None: + # save a timestamp of the last time we checked for retraining to a file + with Path(settings.MODEL_FILE.with_suffix(".last_checked")).open("w") as f: + f.write(str(time.time())) + + def get_last_checked(self) -> float | None: + # load the timestamp of the last time we checked for retraining + try: + with Path(settings.MODEL_FILE.with_suffix(".last_checked")).open("r") as f: + return float(f.read()) + except FileNotFoundError: # pragma: no cover + return None + def save(self) -> None: target_file: Path = settings.MODEL_FILE target_file_temp: Path = target_file.with_suffix(".pickle.part") @@ -161,6 +175,7 @@ class DocumentClassifier: pickle.dump(self.storage_path_classifier, f) target_file_temp.rename(target_file) + self.set_last_checked() def train(self) -> bool: # Get non-inbox documents @@ -229,6 +244,7 @@ class DocumentClassifier: and self.last_doc_change_time >= latest_doc_change ) and self.last_auto_type_hash == hasher.digest(): logger.info("No updates since last training") + self.set_last_checked() # Set the classifier information into the cache # Caching for 50 minutes, so slightly less than the normal retrain time cache.set( diff --git a/src/documents/views.py b/src/documents/views.py index f98932a6f..24578179a 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -15,7 +15,6 @@ from urllib.parse import quote from urllib.parse import urlparse import pathvalidate -from django.apps import apps from django.conf import settings from django.contrib.auth.models import Group from django.contrib.auth.models import User @@ -2174,18 +2173,14 @@ class SystemStatusView(PassUserMixin): classifier_status = "WARNING" raise FileNotFoundError(classifier_error) classifier_status = "OK" - task_result_model = apps.get_model("django_celery_results", "taskresult") - result = ( - task_result_model.objects.filter( - task_name="documents.tasks.train_classifier", - status="SUCCESS", + classifier_last_trained = ( + make_aware( + datetime.fromtimestamp(classifier.get_last_checked()), ) - .order_by( - "-date_done", - ) - .first() + if settings.MODEL_FILE.exists() + and classifier.get_last_checked() is not None + else None ) - classifier_last_trained = result.date_done if result else None except Exception as e: if classifier_status is None: classifier_status = "ERROR" From 046d8456e29d6e96bfd1e150b80015df0c381272 Mon Sep 17 00:00:00 2001 From: XstreamGit Date: Thu, 6 Feb 2025 20:09:35 +0100 Subject: [PATCH 31/37] Tweak: improve date matching regex for dates after numbers (#8964) --- src/documents/parsers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/documents/parsers.py b/src/documents/parsers.py index 2d73dc63f..d840817e4 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -41,7 +41,7 @@ DATE_REGEX = re.compile( r"(\b|(?!=([_-])))(\d{1,2}[\. ]+[a-zéûäëčžúřěáíóńźçŞğü]{3,9} \d{4}|[a-zéûäëčžúřěáíóńźçŞğü]{3,9} \d{1,2}, \d{4})(\b|(?=([_-])))|" r"(\b|(?!=([_-])))([^\W\d_]{3,9} \d{1,2}, (\d{4}))(\b|(?=([_-])))|" r"(\b|(?!=([_-])))([^\W\d_]{3,9} \d{4})(\b|(?=([_-])))|" - r"(\b|(?!=([_-])))(\d{1,2}[^ ]{2}[\. ]+[^ ]{3,9}[ \.\/-]\d{4})(\b|(?=([_-])))|" + r"(\b|(?!=([_-])))(\d{1,2}[^ 0-9]{2}[\. ]+[^ ]{3,9}[ \.\/-]\d{4})(\b|(?=([_-])))|" r"(\b|(?!=([_-])))(\b\d{1,2}[ \.\/-][a-zéûäëčžúřěáíóńźçŞğü]{3}[ \.\/-]\d{4})(\b|(?=([_-])))", re.IGNORECASE, ) From 52ab07c67382508b785033dcc3cbb5ec0669da5c Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:47:50 -0800 Subject: [PATCH 32/37] Fix: deselect and trigger refresh for deleted documents from bulk operations with "delete originals" (#8996) --- src-ui/messages.xlf | 346 ++++++++-------- src-ui/src/app/app.component.spec.ts | 22 +- src-ui/src/app/app.component.ts | 14 +- .../saved-view-widget.component.spec.ts | 14 +- .../saved-view-widget.component.ts | 6 +- .../statistics-widget.component.spec.ts | 10 +- .../statistics-widget.component.ts | 6 +- .../upload-file-widget.component.spec.ts | 26 +- .../upload-file-widget.component.ts | 35 +- .../bulk-editor/bulk-editor.component.spec.ts | 1 + .../bulk-editor/bulk-editor.component.ts | 3 + .../document-list.component.spec.ts | 25 +- .../document-list/document-list.component.ts | 10 +- .../websocket-documents-deleted-message.ts | 3 + ...ssage.ts => websocket-progress-message.ts} | 2 +- .../services/consumer-status.service.spec.ts | 326 --------------- .../services/upload-documents.service.spec.ts | 26 +- .../app/services/upload-documents.service.ts | 16 +- .../services/websocket-status.service.spec.ts | 375 ++++++++++++++++++ ...service.ts => websocket-status.service.ts} | 125 +++--- src/documents/bulk_edit.py | 4 + src/documents/plugins/helpers.py | 46 ++- src/paperless/consumers.py | 8 +- src/paperless/tests/test_websockets.py | 112 +++++- 24 files changed, 897 insertions(+), 664 deletions(-) create mode 100644 src-ui/src/app/data/websocket-documents-deleted-message.ts rename src-ui/src/app/data/{websocket-consumer-status-message.ts => websocket-progress-message.ts} (77%) delete mode 100644 src-ui/src/app/services/consumer-status.service.spec.ts create mode 100644 src-ui/src/app/services/websocket-status.service.spec.ts rename src-ui/src/app/services/{consumer-status.service.ts => websocket-status.service.ts} (71%) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 65e25d8ba..9983e6b55 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -2549,15 +2549,15 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 793 + 796 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 826 + 829 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 845 + 848 src/app/components/manage/custom-fields/custom-fields.component.ts @@ -3143,27 +3143,27 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 436 + 439 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 476 + 479 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 514 + 517 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 552 + 555 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 614 + 617 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 747 + 750 @@ -6143,7 +6143,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 381 + 384 this string is used to separate processing, failed and added on the file upload widget @@ -6676,7 +6676,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 749 + 752 @@ -6687,7 +6687,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 751 + 754 @@ -6705,7 +6705,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 789 + 792 @@ -6786,7 +6786,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 823 + 826 @@ -6982,25 +6982,25 @@ Error executing bulk operation src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 285 + 288 "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 373 + 376 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 379 + 382 "" and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 375 + 378 This is for messages like 'modify "tag1" and "tag2"' @@ -7008,7 +7008,7 @@ and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 383,385 + 386,388 this is for messages like 'modify "tag1", "tag2" and "tag3"' @@ -7016,14 +7016,14 @@ Confirm tags assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 400 + 403 This operation will add the tag "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 406 + 409 @@ -7032,14 +7032,14 @@ )"/> to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 411,413 + 414,416 This operation will remove the tag "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 419 + 422 @@ -7048,7 +7048,7 @@ )"/> from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 424,426 + 427,429 @@ -7059,84 +7059,84 @@ )"/> on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 428,432 + 431,435 Confirm correspondent assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 469 + 472 This operation will assign the correspondent "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 471 + 474 This operation will remove the correspondent from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 473 + 476 Confirm document type assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 507 + 510 This operation will assign the document type "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 509 + 512 This operation will remove the document type from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 511 + 514 Confirm storage path assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 545 + 548 This operation will assign the storage path "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 547 + 550 This operation will remove the storage path from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 549 + 552 Confirm custom field assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 578 + 581 This operation will assign the custom field "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 584 + 587 @@ -7145,14 +7145,14 @@ )"/> to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 589,591 + 592,594 This operation will remove the custom field "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 597 + 600 @@ -7161,7 +7161,7 @@ )"/> from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 602,604 + 605,607 @@ -7172,70 +7172,70 @@ )"/> on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 606,610 + 609,613 Move selected document(s) to the trash? src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 748 + 751 This operation will permanently recreate the archive files for selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 790 + 793 The archive files will be re-generated with the current settings. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 791 + 794 This operation will permanently rotate the original version of document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 824 + 827 Merge confirm src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 843 + 846 This operation will merge selected documents into a new document. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 844 + 847 Merged document will be queued for consumption. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 860 + 863 Custom fields updated. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 882 + 885 Error updating custom fields. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 891 + 894 @@ -7414,7 +7414,7 @@ src/app/components/document-list/document-list.component.ts - 310 + 314 @@ -7425,7 +7425,7 @@ src/app/components/document-list/document-list.component.ts - 303 + 307 @@ -7668,42 +7668,42 @@ Reset filters / selection src/app/components/document-list/document-list.component.ts - 291 + 295 Open first [selected] document src/app/components/document-list/document-list.component.ts - 319 + 323 Previous page src/app/components/document-list/document-list.component.ts - 335 + 339 Next page src/app/components/document-list/document-list.component.ts - 347 + 351 View "" saved successfully. src/app/components/document-list/document-list.component.ts - 379 + 383 View "" created successfully. src/app/components/document-list/document-list.component.ts - 422 + 426 @@ -9233,122 +9233,6 @@ 11 - - Document already exists. - - src/app/services/consumer-status.service.ts - 17 - - - - Document already exists. Note: existing document is in the trash. - - src/app/services/consumer-status.service.ts - 18 - - - - Document with ASN already exists. - - src/app/services/consumer-status.service.ts - 19 - - - - Document with ASN already exists. Note: existing document is in the trash. - - src/app/services/consumer-status.service.ts - 20 - - - - File not found. - - src/app/services/consumer-status.service.ts - 21 - - - - Pre-consume script does not exist. - - src/app/services/consumer-status.service.ts - 22 - - Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation - - - Error while executing pre-consume script. - - src/app/services/consumer-status.service.ts - 23 - - Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation - - - Post-consume script does not exist. - - src/app/services/consumer-status.service.ts - 24 - - Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation - - - Error while executing post-consume script. - - src/app/services/consumer-status.service.ts - 25 - - Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation - - - Received new file. - - src/app/services/consumer-status.service.ts - 26 - - - - File type not supported. - - src/app/services/consumer-status.service.ts - 27 - - - - Processing document... - - src/app/services/consumer-status.service.ts - 28 - - - - Generating thumbnail... - - src/app/services/consumer-status.service.ts - 29 - - - - Retrieving date from document... - - src/app/services/consumer-status.service.ts - 30 - - - - Saving document... - - src/app/services/consumer-status.service.ts - 31 - - - - Finished. - - src/app/services/consumer-status.service.ts - 32 - - You have unsaved changes to the document @@ -9664,6 +9548,122 @@ 70 + + Document already exists. + + src/app/services/websocket-status.service.ts + 23 + + + + Document already exists. Note: existing document is in the trash. + + src/app/services/websocket-status.service.ts + 24 + + + + Document with ASN already exists. + + src/app/services/websocket-status.service.ts + 25 + + + + Document with ASN already exists. Note: existing document is in the trash. + + src/app/services/websocket-status.service.ts + 26 + + + + File not found. + + src/app/services/websocket-status.service.ts + 27 + + + + Pre-consume script does not exist. + + src/app/services/websocket-status.service.ts + 28 + + Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation + + + Error while executing pre-consume script. + + src/app/services/websocket-status.service.ts + 29 + + Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation + + + Post-consume script does not exist. + + src/app/services/websocket-status.service.ts + 30 + + Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation + + + Error while executing post-consume script. + + src/app/services/websocket-status.service.ts + 31 + + Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation + + + Received new file. + + src/app/services/websocket-status.service.ts + 32 + + + + File type not supported. + + src/app/services/websocket-status.service.ts + 33 + + + + Processing document... + + src/app/services/websocket-status.service.ts + 34 + + + + Generating thumbnail... + + src/app/services/websocket-status.service.ts + 35 + + + + Retrieving date from document... + + src/app/services/websocket-status.service.ts + 36 + + + + Saving document... + + src/app/services/websocket-status.service.ts + 37 + + + + Finished. + + src/app/services/websocket-status.service.ts + 38 + + diff --git a/src-ui/src/app/app.component.spec.ts b/src-ui/src/app/app.component.spec.ts index 74626f847..bc59f78dc 100644 --- a/src-ui/src/app/app.component.spec.ts +++ b/src-ui/src/app/app.component.spec.ts @@ -18,20 +18,20 @@ import { ToastsComponent } from './components/common/toasts/toasts.component' import { FileDropComponent } from './components/file-drop/file-drop.component' import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard' import { PermissionsGuard } from './guards/permissions.guard' -import { - ConsumerStatusService, - FileStatus, -} from './services/consumer-status.service' import { HotKeyService } from './services/hot-key.service' import { PermissionsService } from './services/permissions.service' import { SettingsService } from './services/settings.service' import { Toast, ToastService } from './services/toast.service' +import { + FileStatus, + WebsocketStatusService, +} from './services/websocket-status.service' describe('AppComponent', () => { let component: AppComponent let fixture: ComponentFixture let tourService: TourService - let consumerStatusService: ConsumerStatusService + let websocketStatusService: WebsocketStatusService let permissionsService: PermissionsService let toastService: ToastService let router: Router @@ -59,7 +59,7 @@ describe('AppComponent', () => { }).compileComponents() tourService = TestBed.inject(TourService) - consumerStatusService = TestBed.inject(ConsumerStatusService) + websocketStatusService = TestBed.inject(WebsocketStatusService) permissionsService = TestBed.inject(PermissionsService) settingsService = TestBed.inject(SettingsService) toastService = TestBed.inject(ToastService) @@ -90,7 +90,7 @@ describe('AppComponent', () => { const toastSpy = jest.spyOn(toastService, 'show') const fileStatusSubject = new Subject() jest - .spyOn(consumerStatusService, 'onDocumentConsumptionFinished') + .spyOn(websocketStatusService, 'onDocumentConsumptionFinished') .mockReturnValue(fileStatusSubject) component.ngOnInit() const status = new FileStatus() @@ -109,7 +109,7 @@ describe('AppComponent', () => { const toastSpy = jest.spyOn(toastService, 'show') const fileStatusSubject = new Subject() jest - .spyOn(consumerStatusService, 'onDocumentConsumptionFinished') + .spyOn(websocketStatusService, 'onDocumentConsumptionFinished') .mockReturnValue(fileStatusSubject) component.ngOnInit() fileStatusSubject.next(new FileStatus()) @@ -122,7 +122,7 @@ describe('AppComponent', () => { const toastSpy = jest.spyOn(toastService, 'show') const fileStatusSubject = new Subject() jest - .spyOn(consumerStatusService, 'onDocumentDetected') + .spyOn(websocketStatusService, 'onDocumentDetected') .mockReturnValue(fileStatusSubject) component.ngOnInit() fileStatusSubject.next(new FileStatus()) @@ -136,7 +136,7 @@ describe('AppComponent', () => { const toastSpy = jest.spyOn(toastService, 'show') const fileStatusSubject = new Subject() jest - .spyOn(consumerStatusService, 'onDocumentDetected') + .spyOn(websocketStatusService, 'onDocumentDetected') .mockReturnValue(fileStatusSubject) component.ngOnInit() fileStatusSubject.next(new FileStatus()) @@ -148,7 +148,7 @@ describe('AppComponent', () => { const toastSpy = jest.spyOn(toastService, 'showError') const fileStatusSubject = new Subject() jest - .spyOn(consumerStatusService, 'onDocumentConsumptionFailed') + .spyOn(websocketStatusService, 'onDocumentConsumptionFailed') .mockReturnValue(fileStatusSubject) component.ngOnInit() fileStatusSubject.next(new FileStatus()) diff --git a/src-ui/src/app/app.component.ts b/src-ui/src/app/app.component.ts index c89f5d4c2..a6c4702b7 100644 --- a/src-ui/src/app/app.component.ts +++ b/src-ui/src/app/app.component.ts @@ -6,7 +6,6 @@ import { ToastsComponent } from './components/common/toasts/toasts.component' import { FileDropComponent } from './components/file-drop/file-drop.component' import { SETTINGS_KEYS } from './data/ui-settings' import { ComponentRouterService } from './services/component-router.service' -import { ConsumerStatusService } from './services/consumer-status.service' import { HotKeyService } from './services/hot-key.service' import { PermissionAction, @@ -16,6 +15,7 @@ import { import { SettingsService } from './services/settings.service' import { TasksService } from './services/tasks.service' import { ToastService } from './services/toast.service' +import { WebsocketStatusService } from './services/websocket-status.service' @Component({ selector: 'pngx-root', @@ -35,7 +35,7 @@ export class AppComponent implements OnInit, OnDestroy { constructor( private settings: SettingsService, - private consumerStatusService: ConsumerStatusService, + private websocketStatusService: WebsocketStatusService, private toastService: ToastService, private router: Router, private tasksService: TasksService, @@ -51,7 +51,7 @@ export class AppComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { - this.consumerStatusService.disconnect() + this.websocketStatusService.disconnect() if (this.successSubscription) { this.successSubscription.unsubscribe() } @@ -76,9 +76,9 @@ export class AppComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.consumerStatusService.connect() + this.websocketStatusService.connect() - this.successSubscription = this.consumerStatusService + this.successSubscription = this.websocketStatusService .onDocumentConsumptionFinished() .subscribe((status) => { this.tasksService.reload() @@ -108,7 +108,7 @@ export class AppComponent implements OnInit, OnDestroy { } }) - this.failedSubscription = this.consumerStatusService + this.failedSubscription = this.websocketStatusService .onDocumentConsumptionFailed() .subscribe((status) => { this.tasksService.reload() @@ -121,7 +121,7 @@ export class AppComponent implements OnInit, OnDestroy { } }) - this.newDocumentSubscription = this.consumerStatusService + this.newDocumentSubscription = this.websocketStatusService .onDocumentDetected() .subscribe((status) => { this.tasksService.reload() diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts index 5f66c68d6..621a90491 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts @@ -33,14 +33,14 @@ import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe' -import { - ConsumerStatusService, - FileStatus, -} from 'src/app/services/consumer-status.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { PermissionsService } from 'src/app/services/permissions.service' import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' import { DocumentService } from 'src/app/services/rest/document.service' +import { + FileStatus, + WebsocketStatusService, +} from 'src/app/services/websocket-status.service' import { WidgetFrameComponent } from '../widget-frame/widget-frame.component' import { SavedViewWidgetComponent } from './saved-view-widget.component' @@ -112,7 +112,7 @@ describe('SavedViewWidgetComponent', () => { let component: SavedViewWidgetComponent let fixture: ComponentFixture let documentService: DocumentService - let consumerStatusService: ConsumerStatusService + let websocketStatusService: WebsocketStatusService let documentListViewService: DocumentListViewService let router: Router @@ -176,7 +176,7 @@ describe('SavedViewWidgetComponent', () => { }).compileComponents() documentService = TestBed.inject(DocumentService) - consumerStatusService = TestBed.inject(ConsumerStatusService) + websocketStatusService = TestBed.inject(WebsocketStatusService) documentListViewService = TestBed.inject(DocumentListViewService) router = TestBed.inject(Router) fixture = TestBed.createComponent(SavedViewWidgetComponent) @@ -235,7 +235,7 @@ describe('SavedViewWidgetComponent', () => { it('should reload on document consumption finished', () => { const fileStatusSubject = new Subject() jest - .spyOn(consumerStatusService, 'onDocumentConsumptionFinished') + .spyOn(websocketStatusService, 'onDocumentConsumptionFinished') .mockReturnValue(fileStatusSubject) const reloadSpy = jest.spyOn(component, 'reload') component.ngOnInit() diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index 7f6c5755b..32bf7a004 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -42,7 +42,6 @@ import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe' import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe' import { UsernamePipe } from 'src/app/pipes/username.pipe' -import { ConsumerStatusService } from 'src/app/services/consumer-status.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { OpenDocumentsService } from 'src/app/services/open-documents.service' import { @@ -53,6 +52,7 @@ import { import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' import { DocumentService } from 'src/app/services/rest/document.service' import { SettingsService } from 'src/app/services/settings.service' +import { WebsocketStatusService } from 'src/app/services/websocket-status.service' import { WidgetFrameComponent } from '../widget-frame/widget-frame.component' @Component({ @@ -94,7 +94,7 @@ export class SavedViewWidgetComponent private documentService: DocumentService, private router: Router, private list: DocumentListViewService, - private consumerStatusService: ConsumerStatusService, + private websocketStatusService: WebsocketStatusService, public openDocumentsService: OpenDocumentsService, public documentListViewService: DocumentListViewService, public permissionsService: PermissionsService, @@ -124,7 +124,7 @@ export class SavedViewWidgetComponent ngOnInit(): void { this.reload() this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE - this.consumerStatusService + this.websocketStatusService .onDocumentConsumptionFinished() .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { diff --git a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.spec.ts index da0c2c083..48ca50a10 100644 --- a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.spec.ts +++ b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.spec.ts @@ -12,9 +12,9 @@ import { routes } from 'src/app/app-routing.module' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { - ConsumerStatusService, FileStatus, -} from 'src/app/services/consumer-status.service' + WebsocketStatusService, +} from 'src/app/services/websocket-status.service' import { environment } from 'src/environments/environment' import { WidgetFrameComponent } from '../widget-frame/widget-frame.component' import { StatisticsWidgetComponent } from './statistics-widget.component' @@ -23,7 +23,7 @@ describe('StatisticsWidgetComponent', () => { let component: StatisticsWidgetComponent let fixture: ComponentFixture let httpTestingController: HttpTestingController - let consumerStatusService: ConsumerStatusService + let websocketStatusService: WebsocketStatusService const fileStatusSubject = new Subject() beforeEach(async () => { @@ -44,9 +44,9 @@ describe('StatisticsWidgetComponent', () => { }).compileComponents() fixture = TestBed.createComponent(StatisticsWidgetComponent) - consumerStatusService = TestBed.inject(ConsumerStatusService) + websocketStatusService = TestBed.inject(WebsocketStatusService) jest - .spyOn(consumerStatusService, 'onDocumentConsumptionFinished') + .spyOn(websocketStatusService, 'onDocumentConsumptionFinished') .mockReturnValue(fileStatusSubject) component = fixture.componentInstance diff --git a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts index f54852429..0669a3666 100644 --- a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts @@ -8,8 +8,8 @@ import { first, Subject, Subscription, takeUntil } from 'rxjs' import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component' import { FILTER_HAS_TAGS_ANY } from 'src/app/data/filter-rule-type' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' -import { ConsumerStatusService } from 'src/app/services/consumer-status.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' +import { WebsocketStatusService } from 'src/app/services/websocket-status.service' import { environment } from 'src/environments/environment' import { WidgetFrameComponent } from '../widget-frame/widget-frame.component' @@ -51,7 +51,7 @@ export class StatisticsWidgetComponent constructor( private http: HttpClient, - private consumerStatusService: ConsumerStatusService, + private websocketConnectionService: WebsocketStatusService, private documentListViewService: DocumentListViewService ) { super() @@ -109,7 +109,7 @@ export class StatisticsWidgetComponent ngOnInit(): void { this.reload() - this.subscription = this.consumerStatusService + this.subscription = this.websocketConnectionService .onDocumentConsumptionFinished() .subscribe(() => { this.reload() diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts index cc1591966..45ac9217a 100644 --- a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts +++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts @@ -12,13 +12,13 @@ import { NgbAlert, NgbCollapse } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { routes } from 'src/app/app-routing.module' import { PermissionsGuard } from 'src/app/guards/permissions.guard' -import { - ConsumerStatusService, - FileStatus, - FileStatusPhase, -} from 'src/app/services/consumer-status.service' import { PermissionsService } from 'src/app/services/permissions.service' import { UploadDocumentsService } from 'src/app/services/upload-documents.service' +import { + FileStatus, + FileStatusPhase, + WebsocketStatusService, +} from 'src/app/services/websocket-status.service' import { UploadFileWidgetComponent } from './upload-file-widget.component' const FAILED_STATUSES = [new FileStatus()] @@ -42,7 +42,7 @@ const DEFAULT_STATUSES = [ describe('UploadFileWidgetComponent', () => { let component: UploadFileWidgetComponent let fixture: ComponentFixture - let consumerStatusService: ConsumerStatusService + let websocketStatusService: WebsocketStatusService let uploadDocumentsService: UploadDocumentsService beforeEach(async () => { @@ -65,7 +65,7 @@ describe('UploadFileWidgetComponent', () => { ], }).compileComponents() - consumerStatusService = TestBed.inject(ConsumerStatusService) + websocketStatusService = TestBed.inject(WebsocketStatusService) uploadDocumentsService = TestBed.inject(UploadDocumentsService) fixture = TestBed.createComponent(UploadFileWidgetComponent) component = fixture.componentInstance @@ -91,14 +91,14 @@ describe('UploadFileWidgetComponent', () => { }) it('should generate stats summary', () => { - mockConsumerStatuses(consumerStatusService) + mockConsumerStatuses(websocketStatusService) expect(component.getStatusSummary()).toEqual( 'Processing: 6, Failed: 1, Added: 4' ) }) it('should report an upload progress summary', () => { - mockConsumerStatuses(consumerStatusService) + mockConsumerStatuses(websocketStatusService) expect(component.getTotalUploadProgress()).toEqual(0.75) }) @@ -117,7 +117,7 @@ describe('UploadFileWidgetComponent', () => { }) it('should enforce a maximum number of alerts', () => { - mockConsumerStatuses(consumerStatusService) + mockConsumerStatuses(websocketStatusService) fixture.detectChanges() // 5 total, 1 hidden expect(fixture.debugElement.queryAll(By.directive(NgbAlert))).toHaveLength( @@ -131,19 +131,19 @@ describe('UploadFileWidgetComponent', () => { }) it('should allow dismissing an alert', () => { - const dismissSpy = jest.spyOn(consumerStatusService, 'dismiss') + const dismissSpy = jest.spyOn(websocketStatusService, 'dismiss') component.dismiss(new FileStatus()) expect(dismissSpy).toHaveBeenCalled() }) it('should allow dismissing completed alerts', fakeAsync(() => { - mockConsumerStatuses(consumerStatusService) + mockConsumerStatuses(websocketStatusService) component.alertsExpanded = true fixture.detectChanges() jest .spyOn(component, 'getStatusCompleted') .mockImplementation(() => SUCCESS_STATUSES) - const dismissSpy = jest.spyOn(consumerStatusService, 'dismiss') + const dismissSpy = jest.spyOn(websocketStatusService, 'dismiss') component.dismissCompleted() tick(1000) fixture.detectChanges() diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts index f237ab7aa..f60cdce60 100644 --- a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts @@ -12,13 +12,13 @@ import { TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap' import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component' import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' -import { - ConsumerStatusService, - FileStatus, - FileStatusPhase, -} from 'src/app/services/consumer-status.service' import { SettingsService } from 'src/app/services/settings.service' import { UploadDocumentsService } from 'src/app/services/upload-documents.service' +import { + FileStatus, + FileStatusPhase, + WebsocketStatusService, +} from 'src/app/services/websocket-status.service' import { WidgetFrameComponent } from '../widget-frame/widget-frame.component' const MAX_ALERTS = 5 @@ -46,7 +46,7 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions { @ViewChildren(NgbAlert) alerts: QueryList constructor( - private consumerStatusService: ConsumerStatusService, + private websocketStatusService: WebsocketStatusService, private uploadDocumentsService: UploadDocumentsService, public settingsService: SettingsService ) { @@ -54,13 +54,13 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions { } getStatus() { - return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS) + return this.websocketStatusService.getConsumerStatus().slice(0, MAX_ALERTS) } getStatusSummary() { let strings = [] let countUploadingAndProcessing = - this.consumerStatusService.getConsumerStatusNotCompleted().length + this.websocketStatusService.getConsumerStatusNotCompleted().length let countFailed = this.getStatusFailed().length let countSuccess = this.getStatusSuccess().length if (countUploadingAndProcessing > 0) { @@ -78,27 +78,30 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions { } getStatusHidden() { - if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS) + if (this.websocketStatusService.getConsumerStatus().length < MAX_ALERTS) return [] - else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS) + else + return this.websocketStatusService.getConsumerStatus().slice(MAX_ALERTS) } getStatusUploading() { - return this.consumerStatusService.getConsumerStatus( + return this.websocketStatusService.getConsumerStatus( FileStatusPhase.UPLOADING ) } getStatusFailed() { - return this.consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED) + return this.websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED) } getStatusSuccess() { - return this.consumerStatusService.getConsumerStatus(FileStatusPhase.SUCCESS) + return this.websocketStatusService.getConsumerStatus( + FileStatusPhase.SUCCESS + ) } getStatusCompleted() { - return this.consumerStatusService.getConsumerStatusCompleted() + return this.websocketStatusService.getConsumerStatusCompleted() } getTotalUploadProgress() { @@ -134,12 +137,12 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions { } dismiss(status: FileStatus) { - this.consumerStatusService.dismiss(status) + this.websocketStatusService.dismiss(status) } dismissCompleted() { this.getStatusCompleted().forEach((status) => - this.consumerStatusService.dismiss(status) + this.websocketStatusService.dismiss(status) ) } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts index 21b8f4175..aa4a07d12 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts @@ -1039,6 +1039,7 @@ describe('BulkEditorComponent', () => { httpTestingController.match( `${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id` ) // listAllFilteredIds + expect(documentListViewService.selected.size).toEqual(0) }) it('should support bulk download with archive, originals or both and file formatting', () => { diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 5750c4b2f..9864761fa 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -268,6 +268,9 @@ export class BulkEditorComponent .pipe(first()) .subscribe({ next: () => { + if (args['delete_originals']) { + this.list.selected.clear() + } this.list.reload() this.list.reduceSelectionToFilter() this.list.selected.forEach((id) => { diff --git a/src-ui/src/app/components/document-list/document-list.component.spec.ts b/src-ui/src/app/components/document-list/document-list.component.spec.ts index 805a65846..13a938f59 100644 --- a/src-ui/src/app/components/document-list/document-list.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-list.component.spec.ts @@ -38,16 +38,16 @@ import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' import { FilterPipe } from 'src/app/pipes/filter.pipe' import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { UsernamePipe } from 'src/app/pipes/username.pipe' -import { - ConsumerStatusService, - FileStatus, -} from 'src/app/services/consumer-status.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { PermissionsService } from 'src/app/services/permissions.service' import { DocumentService } from 'src/app/services/rest/document.service' import { SavedViewService } from 'src/app/services/rest/saved-view.service' import { SettingsService } from 'src/app/services/settings.service' import { ToastService } from 'src/app/services/toast.service' +import { + FileStatus, + WebsocketStatusService, +} from 'src/app/services/websocket-status.service' import { DocumentCardLargeComponent } from './document-card-large/document-card-large.component' import { DocumentCardSmallComponent } from './document-card-small/document-card-small.component' import { DocumentListComponent } from './document-list.component' @@ -81,7 +81,7 @@ describe('DocumentListComponent', () => { let fixture: ComponentFixture let documentListService: DocumentListViewService let documentService: DocumentService - let consumerStatusService: ConsumerStatusService + let websocketStatusService: WebsocketStatusService let savedViewService: SavedViewService let router: Router let activatedRoute: ActivatedRoute @@ -112,7 +112,7 @@ describe('DocumentListComponent', () => { documentListService = TestBed.inject(DocumentListViewService) documentService = TestBed.inject(DocumentService) - consumerStatusService = TestBed.inject(ConsumerStatusService) + websocketStatusService = TestBed.inject(WebsocketStatusService) savedViewService = TestBed.inject(SavedViewService) router = TestBed.inject(Router) activatedRoute = TestBed.inject(ActivatedRoute) @@ -128,13 +128,24 @@ describe('DocumentListComponent', () => { const reloadSpy = jest.spyOn(documentListService, 'reload') const fileStatusSubject = new Subject() jest - .spyOn(consumerStatusService, 'onDocumentConsumptionFinished') + .spyOn(websocketStatusService, 'onDocumentConsumptionFinished') .mockReturnValue(fileStatusSubject) fixture.detectChanges() fileStatusSubject.next(new FileStatus()) expect(reloadSpy).toHaveBeenCalled() }) + it('should reload on document deleted', () => { + const reloadSpy = jest.spyOn(documentListService, 'reload') + const documentDeletedSubject = new Subject() + jest + .spyOn(websocketStatusService, 'onDocumentDeleted') + .mockReturnValue(documentDeletedSubject) + fixture.detectChanges() + documentDeletedSubject.next(true) + expect(reloadSpy).toHaveBeenCalled() + }) + it('should show score sort fields on fulltext queries', () => { documentListService.filterRules = [ { diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts index b845a524a..e1f71edbc 100644 --- a/src-ui/src/app/components/document-list/document-list.component.ts +++ b/src-ui/src/app/components/document-list/document-list.component.ts @@ -43,7 +43,6 @@ import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe' import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe' import { UsernamePipe } from 'src/app/pipes/username.pipe' -import { ConsumerStatusService } from 'src/app/services/consumer-status.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { HotKeyService } from 'src/app/services/hot-key.service' import { OpenDocumentsService } from 'src/app/services/open-documents.service' @@ -51,6 +50,7 @@ import { PermissionsService } from 'src/app/services/permissions.service' import { SavedViewService } from 'src/app/services/rest/saved-view.service' import { SettingsService } from 'src/app/services/settings.service' import { ToastService } from 'src/app/services/toast.service' +import { WebsocketStatusService } from 'src/app/services/websocket-status.service' import { filterRulesDiffer, isFullTextFilterRule, @@ -113,7 +113,7 @@ export class DocumentListComponent private router: Router, private toastService: ToastService, private modalService: NgbModal, - private consumerStatusService: ConsumerStatusService, + private websocketStatusService: WebsocketStatusService, public openDocumentsService: OpenDocumentsService, public settingsService: SettingsService, private hotKeyService: HotKeyService, @@ -234,13 +234,17 @@ export class DocumentListComponent } ngOnInit(): void { - this.consumerStatusService + this.websocketStatusService .onDocumentConsumptionFinished() .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { this.list.reload() }) + this.websocketStatusService.onDocumentDeleted().subscribe(() => { + this.list.reload() + }) + this.route.paramMap .pipe( filter((params) => params.has('id')), // only on saved view e.g. /view/id diff --git a/src-ui/src/app/data/websocket-documents-deleted-message.ts b/src-ui/src/app/data/websocket-documents-deleted-message.ts new file mode 100644 index 000000000..11ded3781 --- /dev/null +++ b/src-ui/src/app/data/websocket-documents-deleted-message.ts @@ -0,0 +1,3 @@ +export interface WebsocketDocumentsDeletedMessage { + documents: number[] +} diff --git a/src-ui/src/app/data/websocket-consumer-status-message.ts b/src-ui/src/app/data/websocket-progress-message.ts similarity index 77% rename from src-ui/src/app/data/websocket-consumer-status-message.ts rename to src-ui/src/app/data/websocket-progress-message.ts index d1ac590b1..c8e37e232 100644 --- a/src-ui/src/app/data/websocket-consumer-status-message.ts +++ b/src-ui/src/app/data/websocket-progress-message.ts @@ -1,4 +1,4 @@ -export interface WebsocketConsumerStatusMessage { +export interface WebsocketProgressMessage { filename?: string task_id?: string current_progress?: number diff --git a/src-ui/src/app/services/consumer-status.service.spec.ts b/src-ui/src/app/services/consumer-status.service.spec.ts deleted file mode 100644 index b699f8772..000000000 --- a/src-ui/src/app/services/consumer-status.service.spec.ts +++ /dev/null @@ -1,326 +0,0 @@ -import { - HttpEventType, - HttpResponse, - provideHttpClient, - withInterceptorsFromDi, -} from '@angular/common/http' -import { - HttpTestingController, - provideHttpClientTesting, -} from '@angular/common/http/testing' -import { TestBed } from '@angular/core/testing' -import WS from 'jest-websocket-mock' -import { environment } from 'src/environments/environment' -import { - ConsumerStatusService, - FILE_STATUS_MESSAGES, - FileStatusPhase, -} from './consumer-status.service' -import { DocumentService } from './rest/document.service' -import { SettingsService } from './settings.service' - -describe('ConsumerStatusService', () => { - let httpTestingController: HttpTestingController - let consumerStatusService: ConsumerStatusService - let documentService: DocumentService - let settingsService: SettingsService - - const server = new WS( - `${environment.webSocketProtocol}//${environment.webSocketHost}${environment.webSocketBaseUrl}status/`, - { jsonProtocol: true } - ) - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [], - providers: [ - ConsumerStatusService, - DocumentService, - SettingsService, - provideHttpClient(withInterceptorsFromDi()), - provideHttpClientTesting(), - ], - }) - - httpTestingController = TestBed.inject(HttpTestingController) - settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { - id: 1, - username: 'testuser', - is_superuser: false, - } - consumerStatusService = TestBed.inject(ConsumerStatusService) - documentService = TestBed.inject(DocumentService) - }) - - afterEach(() => { - httpTestingController.verify() - }) - - it('should update status on websocket processing progress', () => { - const task_id = '1234' - const status = consumerStatusService.newFileUpload('file.pdf') - expect(status.getProgress()).toEqual(0) - - consumerStatusService.connect() - - consumerStatusService - .onDocumentConsumptionFinished() - .subscribe((filestatus) => { - expect(filestatus.phase).toEqual(FileStatusPhase.SUCCESS) - }) - - consumerStatusService.onDocumentDetected().subscribe((filestatus) => { - expect(filestatus.phase).toEqual(FileStatusPhase.STARTED) - }) - - server.send({ - task_id, - filename: 'file.pdf', - current_progress: 50, - max_progress: 100, - document_id: 12, - status: 'WORKING', - }) - - expect(status.getProgress()).toBeCloseTo(0.6) // (0.8 * 50/100) + .2 - expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([ - status, - ]) - - server.send({ - task_id, - filename: 'file.pdf', - current_progress: 100, - max_progress: 100, - document_id: 12, - status: 'SUCCESS', - message: FILE_STATUS_MESSAGES.finished, - }) - - expect(status.getProgress()).toEqual(1) - expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength( - 0 - ) - expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1) - - consumerStatusService.disconnect() - }) - - it('should update status on websocket failed progress', () => { - const task_id = '1234' - const status = consumerStatusService.newFileUpload('file.pdf') - status.taskId = task_id - consumerStatusService.connect() - - consumerStatusService - .onDocumentConsumptionFailed() - .subscribe((filestatus) => { - expect(filestatus.phase).toEqual(FileStatusPhase.FAILED) - }) - - server.send({ - task_id, - filename: 'file.pdf', - current_progress: 50, - max_progress: 100, - document_id: 12, - }) - - expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([ - status, - ]) - - server.send({ - task_id, - filename: 'file.pdf', - current_progress: 50, - max_progress: 100, - document_id: 12, - status: 'FAILED', - message: FILE_STATUS_MESSAGES.document_already_exists, - }) - - expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength( - 0 - ) - expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1) - }) - - it('should update status on upload progress', () => { - const task_id = '1234' - const status = consumerStatusService.newFileUpload('file.pdf') - - documentService.uploadDocument({}).subscribe((event) => { - if (event.type === HttpEventType.Response) { - status.taskId = event.body['task_id'] - status.message = $localize`Upload complete, waiting...` - } else if (event.type === HttpEventType.UploadProgress) { - status.updateProgress( - FileStatusPhase.UPLOADING, - event.loaded, - event.total - ) - } - }) - - const req = httpTestingController.expectOne( - `${environment.apiBaseUrl}documents/post_document/` - ) - - req.event( - new HttpResponse({ - body: { - task_id, - }, - }) - ) - - req.event({ - type: HttpEventType.UploadProgress, - loaded: 100, - total: 300, - }) - - expect( - consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) - ).toEqual([status]) - expect(consumerStatusService.getConsumerStatus()).toEqual([status]) - expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([ - status, - ]) - - req.event({ - type: HttpEventType.UploadProgress, - loaded: 300, - total: 300, - }) - - expect(status.getProgress()).toEqual(0.2) // 0.2 * 300/300 - }) - - it('should support dismiss completed', () => { - consumerStatusService.connect() - server.send({ - task_id: '1234', - filename: 'file.pdf', - current_progress: 100, - max_progress: 100, - document_id: 12, - status: 'SUCCESS', - message: 'finished', - }) - - expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1) - consumerStatusService.dismissCompleted() - expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(0) - consumerStatusService.disconnect() - }) - - it('should support dismiss', () => { - const task_id = '1234' - const status = consumerStatusService.newFileUpload('file.pdf') - status.taskId = task_id - status.updateProgress(FileStatusPhase.UPLOADING, 50, 100) - - const status2 = consumerStatusService.newFileUpload('file2.pdf') - status2.updateProgress(FileStatusPhase.UPLOADING, 50, 100) - - expect( - consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) - ).toEqual([status, status2]) - expect(consumerStatusService.getConsumerStatus()).toEqual([status, status2]) - expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([ - status, - status2, - ]) - - consumerStatusService.dismiss(status) - expect(consumerStatusService.getConsumerStatus()).toEqual([status2]) - - consumerStatusService.dismiss(status2) - expect(consumerStatusService.getConsumerStatus()).toHaveLength(0) - }) - - it('should support fail', () => { - const task_id = '1234' - const status = consumerStatusService.newFileUpload('file.pdf') - status.taskId = task_id - status.updateProgress(FileStatusPhase.UPLOADING, 50, 100) - expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength( - 1 - ) - expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(0) - consumerStatusService.fail(status, 'fail') - expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength( - 0 - ) - expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1) - }) - - it('should notify of document created on status message without upload', () => { - let detected = false - consumerStatusService.onDocumentDetected().subscribe((filestatus) => { - expect(filestatus.phase).toEqual(FileStatusPhase.STARTED) - detected = true - }) - - consumerStatusService.connect() - server.send({ - task_id: '1234', - filename: 'file.pdf', - current_progress: 0, - max_progress: 100, - message: 'new_file', - status: 'STARTED', - }) - - consumerStatusService.disconnect() - expect(detected).toBeTruthy() - }) - - it('should notify of document in progress without upload', () => { - consumerStatusService.connect() - server.send({ - task_id: '1234', - filename: 'file.pdf', - current_progress: 50, - max_progress: 100, - docuement_id: 12, - status: 'WORKING', - }) - - consumerStatusService.disconnect() - expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength( - 1 - ) - }) - - it('should not notify current user if document has different expected owner', () => { - consumerStatusService.connect() - server.send({ - task_id: '1234', - filename: 'file1.pdf', - current_progress: 50, - max_progress: 100, - docuement_id: 12, - owner_id: 1, - status: 'WORKING', - }) - - server.send({ - task_id: '5678', - filename: 'file2.pdf', - current_progress: 50, - max_progress: 100, - docuement_id: 13, - owner_id: 2, - status: 'WORKING', - }) - - consumerStatusService.disconnect() - expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength( - 1 - ) - }) -}) diff --git a/src-ui/src/app/services/upload-documents.service.spec.ts b/src-ui/src/app/services/upload-documents.service.spec.ts index cf0812306..28fb5b2e0 100644 --- a/src-ui/src/app/services/upload-documents.service.spec.ts +++ b/src-ui/src/app/services/upload-documents.service.spec.ts @@ -9,11 +9,11 @@ import { } from '@angular/common/http/testing' import { TestBed } from '@angular/core/testing' import { environment } from 'src/environments/environment' -import { - ConsumerStatusService, - FileStatusPhase, -} from './consumer-status.service' import { UploadDocumentsService } from './upload-documents.service' +import { + FileStatusPhase, + WebsocketStatusService, +} from './websocket-status.service' const files = [ { @@ -45,14 +45,14 @@ const fileList = { describe('UploadDocumentsService', () => { let httpTestingController: HttpTestingController let uploadDocumentsService: UploadDocumentsService - let consumerStatusService: ConsumerStatusService + let websocketStatusService: WebsocketStatusService beforeEach(() => { TestBed.configureTestingModule({ imports: [], providers: [ UploadDocumentsService, - ConsumerStatusService, + WebsocketStatusService, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), ], @@ -60,7 +60,7 @@ describe('UploadDocumentsService', () => { httpTestingController = TestBed.inject(HttpTestingController) uploadDocumentsService = TestBed.inject(UploadDocumentsService) - consumerStatusService = TestBed.inject(ConsumerStatusService) + websocketStatusService = TestBed.inject(WebsocketStatusService) }) afterEach(() => { @@ -80,11 +80,11 @@ describe('UploadDocumentsService', () => { it('updates progress during upload and failure', () => { uploadDocumentsService.uploadFiles(fileList) - expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength( + expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength( 2 ) expect( - consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) + websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) ).toHaveLength(0) const req = httpTestingController.match( @@ -98,7 +98,7 @@ describe('UploadDocumentsService', () => { }) expect( - consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) + websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) ).toHaveLength(1) }) @@ -110,7 +110,7 @@ describe('UploadDocumentsService', () => { ) expect( - consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED) + websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED) ).toHaveLength(0) req[0].flush( @@ -122,7 +122,7 @@ describe('UploadDocumentsService', () => { ) expect( - consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED) + websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED) ).toHaveLength(1) uploadDocumentsService.uploadFiles(fileList) @@ -140,7 +140,7 @@ describe('UploadDocumentsService', () => { ) expect( - consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED) + websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED) ).toHaveLength(2) }) diff --git a/src-ui/src/app/services/upload-documents.service.ts b/src-ui/src/app/services/upload-documents.service.ts index 8a5e42b47..602e6d8ae 100644 --- a/src-ui/src/app/services/upload-documents.service.ts +++ b/src-ui/src/app/services/upload-documents.service.ts @@ -2,11 +2,11 @@ import { HttpEventType } from '@angular/common/http' import { Injectable } from '@angular/core' import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop' import { Subscription } from 'rxjs' -import { - ConsumerStatusService, - FileStatusPhase, -} from './consumer-status.service' import { DocumentService } from './rest/document.service' +import { + FileStatusPhase, + WebsocketStatusService, +} from './websocket-status.service' @Injectable({ providedIn: 'root', @@ -16,7 +16,7 @@ export class UploadDocumentsService { constructor( private documentService: DocumentService, - private consumerStatusService: ConsumerStatusService + private websocketStatusService: WebsocketStatusService ) {} onNgxFileDrop(files: NgxFileDropEntry[]) { @@ -37,7 +37,7 @@ export class UploadDocumentsService { private uploadFile(file: File) { let formData = new FormData() formData.append('document', file, file.name) - let status = this.consumerStatusService.newFileUpload(file.name) + let status = this.websocketStatusService.newFileUpload(file.name) status.message = $localize`Connecting...` @@ -61,11 +61,11 @@ export class UploadDocumentsService { error: (error) => { switch (error.status) { case 400: { - this.consumerStatusService.fail(status, error.error.document) + this.websocketStatusService.fail(status, error.error.document) break } default: { - this.consumerStatusService.fail( + this.websocketStatusService.fail( status, $localize`HTTP error: ${error.status} ${error.statusText}` ) diff --git a/src-ui/src/app/services/websocket-status.service.spec.ts b/src-ui/src/app/services/websocket-status.service.spec.ts new file mode 100644 index 000000000..d3bf71f7e --- /dev/null +++ b/src-ui/src/app/services/websocket-status.service.spec.ts @@ -0,0 +1,375 @@ +import { + HttpEventType, + HttpResponse, + provideHttpClient, + withInterceptorsFromDi, +} from '@angular/common/http' +import { + HttpTestingController, + provideHttpClientTesting, +} from '@angular/common/http/testing' +import { TestBed } from '@angular/core/testing' +import WS from 'jest-websocket-mock' +import { environment } from 'src/environments/environment' +import { DocumentService } from './rest/document.service' +import { SettingsService } from './settings.service' +import { + FILE_STATUS_MESSAGES, + FileStatusPhase, + WebsocketStatusService, + WebsocketStatusType, +} from './websocket-status.service' + +describe('ConsumerStatusService', () => { + let httpTestingController: HttpTestingController + let websocketStatusService: WebsocketStatusService + let documentService: DocumentService + let settingsService: SettingsService + + const server = new WS( + `${environment.webSocketProtocol}//${environment.webSocketHost}${environment.webSocketBaseUrl}status/`, + { jsonProtocol: true } + ) + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [], + providers: [ + WebsocketStatusService, + DocumentService, + SettingsService, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], + }) + + httpTestingController = TestBed.inject(HttpTestingController) + settingsService = TestBed.inject(SettingsService) + settingsService.currentUser = { + id: 1, + username: 'testuser', + is_superuser: false, + } + websocketStatusService = TestBed.inject(WebsocketStatusService) + documentService = TestBed.inject(DocumentService) + }) + + afterEach(() => { + httpTestingController.verify() + }) + + it('should update status on websocket processing progress', () => { + const task_id = '1234' + const status = websocketStatusService.newFileUpload('file.pdf') + expect(status.getProgress()).toEqual(0) + + websocketStatusService.connect() + + websocketStatusService + .onDocumentConsumptionFinished() + .subscribe((filestatus) => { + expect(filestatus.phase).toEqual(FileStatusPhase.SUCCESS) + }) + + websocketStatusService.onDocumentDetected().subscribe((filestatus) => { + expect(filestatus.phase).toEqual(FileStatusPhase.STARTED) + }) + + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id, + filename: 'file.pdf', + current_progress: 50, + max_progress: 100, + document_id: 12, + status: 'WORKING', + }, + }) + + expect(status.getProgress()).toBeCloseTo(0.6) // (0.8 * 50/100) + .2 + expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([ + status, + ]) + + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id, + filename: 'file.pdf', + current_progress: 100, + max_progress: 100, + document_id: 12, + status: 'SUCCESS', + message: FILE_STATUS_MESSAGES.finished, + }, + }) + + expect(status.getProgress()).toEqual(1) + expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength( + 0 + ) + expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1) + + websocketStatusService.disconnect() + }) + + it('should update status on websocket failed progress', () => { + const task_id = '1234' + const status = websocketStatusService.newFileUpload('file.pdf') + status.taskId = task_id + websocketStatusService.connect() + + websocketStatusService + .onDocumentConsumptionFailed() + .subscribe((filestatus) => { + expect(filestatus.phase).toEqual(FileStatusPhase.FAILED) + }) + + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id, + filename: 'file.pdf', + current_progress: 50, + max_progress: 100, + document_id: 12, + }, + }) + + expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([ + status, + ]) + + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id, + filename: 'file.pdf', + current_progress: 50, + max_progress: 100, + document_id: 12, + status: 'FAILED', + message: FILE_STATUS_MESSAGES.document_already_exists, + }, + }) + + expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength( + 0 + ) + expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1) + }) + + it('should update status on upload progress', () => { + const task_id = '1234' + const status = websocketStatusService.newFileUpload('file.pdf') + + documentService.uploadDocument({}).subscribe((event) => { + if (event.type === HttpEventType.Response) { + status.taskId = event.body['task_id'] + status.message = $localize`Upload complete, waiting...` + } else if (event.type === HttpEventType.UploadProgress) { + status.updateProgress( + FileStatusPhase.UPLOADING, + event.loaded, + event.total + ) + } + }) + + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}documents/post_document/` + ) + + req.event( + new HttpResponse({ + body: { + task_id, + }, + }) + ) + + req.event({ + type: HttpEventType.UploadProgress, + loaded: 100, + total: 300, + }) + + expect( + websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) + ).toEqual([status]) + expect(websocketStatusService.getConsumerStatus()).toEqual([status]) + expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([ + status, + ]) + + req.event({ + type: HttpEventType.UploadProgress, + loaded: 300, + total: 300, + }) + + expect(status.getProgress()).toEqual(0.2) // 0.2 * 300/300 + }) + + it('should support dismiss completed', () => { + websocketStatusService.connect() + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id: '1234', + filename: 'file.pdf', + current_progress: 100, + max_progress: 100, + document_id: 12, + status: 'SUCCESS', + message: 'finished', + }, + }) + + expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1) + websocketStatusService.dismissCompleted() + expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(0) + websocketStatusService.disconnect() + }) + + it('should support dismiss', () => { + const task_id = '1234' + const status = websocketStatusService.newFileUpload('file.pdf') + status.taskId = task_id + status.updateProgress(FileStatusPhase.UPLOADING, 50, 100) + + const status2 = websocketStatusService.newFileUpload('file2.pdf') + status2.updateProgress(FileStatusPhase.UPLOADING, 50, 100) + + expect( + websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING) + ).toEqual([status, status2]) + expect(websocketStatusService.getConsumerStatus()).toEqual([ + status, + status2, + ]) + expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([ + status, + status2, + ]) + + websocketStatusService.dismiss(status) + expect(websocketStatusService.getConsumerStatus()).toEqual([status2]) + + websocketStatusService.dismiss(status2) + expect(websocketStatusService.getConsumerStatus()).toHaveLength(0) + }) + + it('should support fail', () => { + const task_id = '1234' + const status = websocketStatusService.newFileUpload('file.pdf') + status.taskId = task_id + status.updateProgress(FileStatusPhase.UPLOADING, 50, 100) + expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength( + 1 + ) + expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(0) + websocketStatusService.fail(status, 'fail') + expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength( + 0 + ) + expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1) + }) + + it('should notify of document created on status message without upload', () => { + let detected = false + websocketStatusService.onDocumentDetected().subscribe((filestatus) => { + expect(filestatus.phase).toEqual(FileStatusPhase.STARTED) + detected = true + }) + + websocketStatusService.connect() + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id: '1234', + filename: 'file.pdf', + current_progress: 0, + max_progress: 100, + message: 'new_file', + status: 'STARTED', + }, + }) + + websocketStatusService.disconnect() + expect(detected).toBeTruthy() + }) + + it('should notify of document in progress without upload', () => { + websocketStatusService.connect() + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id: '1234', + filename: 'file.pdf', + current_progress: 50, + max_progress: 100, + docuement_id: 12, + status: 'WORKING', + }, + }) + + websocketStatusService.disconnect() + expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength( + 1 + ) + }) + + it('should not notify current user if document has different expected owner', () => { + websocketStatusService.connect() + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id: '1234', + filename: 'file1.pdf', + current_progress: 50, + max_progress: 100, + docuement_id: 12, + owner_id: 1, + status: 'WORKING', + }, + }) + + server.send({ + type: WebsocketStatusType.STATUS_UPDATE, + data: { + task_id: '5678', + filename: 'file2.pdf', + current_progress: 50, + max_progress: 100, + docuement_id: 13, + owner_id: 2, + status: 'WORKING', + }, + }) + + websocketStatusService.disconnect() + expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength( + 1 + ) + }) + + it('should trigger deleted subject on document deleted', () => { + let deleted = false + websocketStatusService.onDocumentDeleted().subscribe(() => { + deleted = true + }) + + websocketStatusService.connect() + server.send({ + type: WebsocketStatusType.DOCUMENTS_DELETED, + data: { + documents: [1, 2, 3], + }, + }) + + websocketStatusService.disconnect() + expect(deleted).toBeTruthy() + }) +}) diff --git a/src-ui/src/app/services/consumer-status.service.ts b/src-ui/src/app/services/websocket-status.service.ts similarity index 71% rename from src-ui/src/app/services/consumer-status.service.ts rename to src-ui/src/app/services/websocket-status.service.ts index 40641ff81..13f82412f 100644 --- a/src-ui/src/app/services/consumer-status.service.ts +++ b/src-ui/src/app/services/websocket-status.service.ts @@ -1,9 +1,15 @@ import { Injectable } from '@angular/core' import { Subject } from 'rxjs' import { environment } from 'src/environments/environment' -import { WebsocketConsumerStatusMessage } from '../data/websocket-consumer-status-message' +import { WebsocketDocumentsDeletedMessage } from '../data/websocket-documents-deleted-message' +import { WebsocketProgressMessage } from '../data/websocket-progress-message' import { SettingsService } from './settings.service' +export enum WebsocketStatusType { + STATUS_UPDATE = 'status_update', + DOCUMENTS_DELETED = 'documents_deleted', +} + // see ProgressStatusOptions in src/documents/plugins/helpers.py export enum FileStatusPhase { STARTED = 0, @@ -85,7 +91,7 @@ export class FileStatus { @Injectable({ providedIn: 'root', }) -export class ConsumerStatusService { +export class WebsocketStatusService { constructor(private settingsService: SettingsService) {} private statusWebSocket: WebSocket @@ -95,6 +101,7 @@ export class ConsumerStatusService { private documentDetectedSubject = new Subject() private documentConsumptionFinishedSubject = new Subject() private documentConsumptionFailedSubject = new Subject() + private documentDeletedSubject = new Subject() private get(taskId: string, filename?: string) { let status = @@ -145,63 +152,75 @@ export class ConsumerStatusService { this.statusWebSocket = new WebSocket( `${environment.webSocketProtocol}//${environment.webSocketHost}${environment.webSocketBaseUrl}status/` ) - this.statusWebSocket.onmessage = (ev) => { - let statusMessage: WebsocketConsumerStatusMessage = JSON.parse(ev['data']) + this.statusWebSocket.onmessage = (ev: MessageEvent) => { + const { + type, + data: messageData, + }: { + type: WebsocketStatusType + data: WebsocketProgressMessage | WebsocketDocumentsDeletedMessage + } = JSON.parse(ev.data) - // fallback if backend didn't restrict message - if ( - statusMessage.owner_id && - statusMessage.owner_id !== this.settingsService.currentUser?.id && - !this.settingsService.currentUser?.is_superuser - ) { - return - } - - let statusMessageGet = this.get( - statusMessage.task_id, - statusMessage.filename - ) - let status = statusMessageGet.status - let created = statusMessageGet.created - - status.updateProgress( - FileStatusPhase.WORKING, - statusMessage.current_progress, - statusMessage.max_progress - ) - if ( - statusMessage.message && - statusMessage.message in FILE_STATUS_MESSAGES - ) { - status.message = FILE_STATUS_MESSAGES[statusMessage.message] - } else if (statusMessage.message) { - status.message = statusMessage.message - } - status.documentId = statusMessage.document_id - - if (statusMessage.status in FileStatusPhase) { - status.phase = FileStatusPhase[statusMessage.status] - } - - switch (status.phase) { - case FileStatusPhase.STARTED: - if (created) this.documentDetectedSubject.next(status) + switch (type) { + case WebsocketStatusType.DOCUMENTS_DELETED: + this.documentDeletedSubject.next(true) break - case FileStatusPhase.SUCCESS: - this.documentConsumptionFinishedSubject.next(status) - break - - case FileStatusPhase.FAILED: - this.documentConsumptionFailedSubject.next(status) - break - - default: + case WebsocketStatusType.STATUS_UPDATE: + this.handleProgressUpdate(messageData as WebsocketProgressMessage) break } } } + handleProgressUpdate(messageData: WebsocketProgressMessage) { + // fallback if backend didn't restrict message + if ( + messageData.owner_id && + messageData.owner_id !== this.settingsService.currentUser?.id && + !this.settingsService.currentUser?.is_superuser + ) { + return + } + + let statusMessageGet = this.get(messageData.task_id, messageData.filename) + let status = statusMessageGet.status + let created = statusMessageGet.created + + status.updateProgress( + FileStatusPhase.WORKING, + messageData.current_progress, + messageData.max_progress + ) + if (messageData.message && messageData.message in FILE_STATUS_MESSAGES) { + status.message = FILE_STATUS_MESSAGES[messageData.message] + } else if (messageData.message) { + status.message = messageData.message + } + status.documentId = messageData.document_id + + if (messageData.status in FileStatusPhase) { + status.phase = FileStatusPhase[messageData.status] + } + + switch (status.phase) { + case FileStatusPhase.STARTED: + if (created) this.documentDetectedSubject.next(status) + break + + case FileStatusPhase.SUCCESS: + this.documentConsumptionFinishedSubject.next(status) + break + + case FileStatusPhase.FAILED: + this.documentConsumptionFailedSubject.next(status) + break + + default: + break + } + } + fail(status: FileStatus, message: string) { status.message = message status.phase = FileStatusPhase.FAILED @@ -250,4 +269,8 @@ export class ConsumerStatusService { onDocumentDetected() { return this.documentDetectedSubject } + + onDocumentDeleted() { + return this.documentDeletedSubject + } } diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index f0522eddc..0aadcc295 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -24,6 +24,7 @@ from documents.models import Document from documents.models import DocumentType from documents.models import StoragePath from documents.permissions import set_permissions_for_object +from documents.plugins.helpers import DocumentsStatusManager from documents.tasks import bulk_update_documents from documents.tasks import consume_file from documents.tasks import update_document_content_maybe_archive_file @@ -219,6 +220,9 @@ def delete(doc_ids: list[int]) -> Literal["OK"]: with index.open_index_writer() as writer: for id in doc_ids: index.remove_document_by_id(writer, id) + + status_mgr = DocumentsStatusManager() + status_mgr.send_documents_deleted(doc_ids) except Exception as e: if "Data too long for column" in str(e): logger.warning( diff --git a/src/documents/plugins/helpers.py b/src/documents/plugins/helpers.py index 20380b852..3315ec60e 100644 --- a/src/documents/plugins/helpers.py +++ b/src/documents/plugins/helpers.py @@ -15,16 +15,14 @@ class ProgressStatusOptions(str, enum.Enum): FAILED = "FAILED" -class ProgressManager: +class BaseStatusManager: """ Handles sending of progress information via the channel layer, with proper management of the open/close of the layer to ensure messages go out and everything is cleaned up """ - def __init__(self, filename: str, task_id: str | None = None) -> None: - self.filename = filename + def __init__(self) -> None: self._channel: RedisPubSubChannelLayer | None = None - self.task_id = task_id def __enter__(self): self.open() @@ -49,6 +47,24 @@ class ProgressManager: async_to_sync(self._channel.flush) self._channel = None + def send(self, payload: dict[str, str | int | None]) -> None: + # Ensure the layer is open + self.open() + + # Just for IDEs + if TYPE_CHECKING: + assert self._channel is not None + + # Construct and send the update + async_to_sync(self._channel.group_send)("status_updates", payload) + + +class ProgressManager(BaseStatusManager): + def __init__(self, filename: str | None = None, task_id: str | None = None) -> None: + super().__init__() + self.filename = filename + self.task_id = task_id + def send_progress( self, status: ProgressStatusOptions, @@ -57,13 +73,6 @@ class ProgressManager: max_progress: int, extra_args: dict[str, str | int | None] | None = None, ) -> None: - # Ensure the layer is open - self.open() - - # Just for IDEs - if TYPE_CHECKING: - assert self._channel is not None - payload = { "type": "status_update", "data": { @@ -78,5 +87,16 @@ class ProgressManager: if extra_args is not None: payload["data"].update(extra_args) - # Construct and send the update - async_to_sync(self._channel.group_send)("status_updates", payload) + self.send(payload) + + +class DocumentsStatusManager(BaseStatusManager): + def send_documents_deleted(self, documents: list[int]) -> None: + payload = { + "type": "documents_deleted", + "data": { + "documents": documents, + }, + } + + self.send(payload) diff --git a/src/paperless/consumers.py b/src/paperless/consumers.py index cf1a3b548..c72b58aa7 100644 --- a/src/paperless/consumers.py +++ b/src/paperless/consumers.py @@ -41,4 +41,10 @@ class StatusConsumer(WebsocketConsumer): self.close() else: if self._is_owner_or_unowned(event["data"]): - self.send(json.dumps(event["data"])) + self.send(json.dumps(event)) + + def documents_deleted(self, event): + if not self._authenticated(): + self.close() + else: + self.send(json.dumps(event)) diff --git a/src/paperless/tests/test_websockets.py b/src/paperless/tests/test_websockets.py index bf838821a..5ba909d1c 100644 --- a/src/paperless/tests/test_websockets.py +++ b/src/paperless/tests/test_websockets.py @@ -5,6 +5,9 @@ from channels.testing import WebsocketCommunicator from django.test import TestCase from django.test import override_settings +from documents.plugins.helpers import DocumentsStatusManager +from documents.plugins.helpers import ProgressManager +from documents.plugins.helpers import ProgressStatusOptions from paperless.asgi import application TEST_CHANNEL_LAYERS = { @@ -22,6 +25,39 @@ class TestWebSockets(TestCase): self.assertFalse(connected) await communicator.disconnect() + @mock.patch("paperless.consumers.StatusConsumer.close") + @mock.patch("paperless.consumers.StatusConsumer._authenticated") + async def test_close_on_no_auth(self, _authenticated, mock_close): + _authenticated.return_value = True + + communicator = WebsocketCommunicator(application, "/ws/status/") + connected, subprotocol = await communicator.connect() + self.assertTrue(connected) + + message = {"type": "status_update", "data": {"task_id": "test"}} + + _authenticated.return_value = False + + channel_layer = get_channel_layer() + await channel_layer.group_send( + "status_updates", + message, + ) + await communicator.receive_nothing() + + mock_close.assert_called_once() + mock_close.reset_mock() + + message = {"type": "documents_deleted", "data": {"documents": [1, 2, 3]}} + + await channel_layer.group_send( + "status_updates", + message, + ) + await communicator.receive_nothing() + + mock_close.assert_called_once() + @mock.patch("paperless.consumers.StatusConsumer._authenticated") async def test_auth(self, _authenticated): _authenticated.return_value = True @@ -33,19 +69,19 @@ class TestWebSockets(TestCase): await communicator.disconnect() @mock.patch("paperless.consumers.StatusConsumer._authenticated") - async def test_receive(self, _authenticated): + async def test_receive_status_update(self, _authenticated): _authenticated.return_value = True communicator = WebsocketCommunicator(application, "/ws/status/") connected, subprotocol = await communicator.connect() self.assertTrue(connected) - message = {"task_id": "test"} + message = {"type": "status_update", "data": {"task_id": "test"}} channel_layer = get_channel_layer() await channel_layer.group_send( "status_updates", - {"type": "status_update", "data": message}, + message, ) response = await communicator.receive_json_from() @@ -53,3 +89,73 @@ class TestWebSockets(TestCase): self.assertEqual(response, message) await communicator.disconnect() + + @mock.patch("paperless.consumers.StatusConsumer._authenticated") + async def test_receive_documents_deleted(self, _authenticated): + _authenticated.return_value = True + + communicator = WebsocketCommunicator(application, "/ws/status/") + connected, subprotocol = await communicator.connect() + self.assertTrue(connected) + + message = {"type": "documents_deleted", "data": {"documents": [1, 2, 3]}} + + channel_layer = get_channel_layer() + await channel_layer.group_send( + "status_updates", + message, + ) + + response = await communicator.receive_json_from() + + self.assertEqual(response, message) + + await communicator.disconnect() + + @mock.patch("channels.layers.InMemoryChannelLayer.group_send") + def test_manager_send_progress(self, mock_group_send): + with ProgressManager(task_id="test") as manager: + manager.send_progress( + ProgressStatusOptions.STARTED, + "Test message", + 1, + 10, + extra_args={ + "foo": "bar", + }, + ) + + message = mock_group_send.call_args[0][1] + + self.assertEqual( + message, + { + "type": "status_update", + "data": { + "filename": None, + "task_id": "test", + "current_progress": 1, + "max_progress": 10, + "status": ProgressStatusOptions.STARTED, + "message": "Test message", + "foo": "bar", + }, + }, + ) + + @mock.patch("channels.layers.InMemoryChannelLayer.group_send") + def test_manager_send_documents_deleted(self, mock_group_send): + with DocumentsStatusManager() as manager: + manager.send_documents_deleted([1, 2, 3]) + + message = mock_group_send.call_args[0][1] + + self.assertEqual( + message, + { + "type": "documents_deleted", + "data": { + "documents": [1, 2, 3], + }, + }, + ) From e08606af6e686ab076850c5fae1aa162a6cbe0d5 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:01:48 -0800 Subject: [PATCH 33/37] Enhancement: date picker and date filter dropdown improvements (#9033) --- .../e2e/document-list/document-list.spec.ts | 4 +- .../requests/api-document-list3.har | 4 +- src-ui/messages.xlf | 219 +++++++++++------- ...ustom-fields-query-dropdown.component.html | 9 +- ...ustom-fields-query-dropdown.component.scss | 6 + .../custom-fields-query-dropdown.component.ts | 2 + .../dates-dropdown.component.html | 66 ++++-- .../dates-dropdown.component.scss | 6 + .../dates-dropdown.component.spec.ts | 70 +++--- .../dates-dropdown.component.ts | 114 ++++----- .../common/input/date/date.component.html | 8 +- .../common/input/date/date.component.scss | 5 + .../common/input/date/date.component.ts | 2 + .../filter-editor.component.html | 8 +- .../filter-editor.component.spec.ts | 92 ++++++-- .../filter-editor/filter-editor.component.ts | 84 ++++--- src-ui/src/app/data/filter-rule-type.ts | 29 +++ src/documents/filters.py | 14 +- ...062_alter_savedviewfilterrule_rule_type.py | 69 ++++++ src/documents/models.py | 4 + 20 files changed, 561 insertions(+), 254 deletions(-) create mode 100644 src/documents/migrations/1062_alter_savedviewfilterrule_rule_type.py diff --git a/src-ui/e2e/document-list/document-list.spec.ts b/src-ui/e2e/document-list/document-list.spec.ts index 7719873d3..cd1a4c54e 100644 --- a/src-ui/e2e/document-list/document-list.spec.ts +++ b/src-ui/e2e/document-list/document-list.spec.ts @@ -83,9 +83,9 @@ test('date filtering', async ({ page }) => { await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' }) await page.goto('/documents') await page.getByRole('button', { name: 'Dates' }).click() - await page.getByRole('menuitem', { name: 'Last 3 months' }).first().click() + await page.getByRole('menuitem', { name: 'Within 3 months' }).first().click() await expect(page.locator('pngx-document-list')).toHaveText(/one document/i) - await page.getByRole('menuitem', { name: 'Last 3 months' }).first().click() + await page.getByRole('menuitem', { name: 'Within 3 months' }).first().click() await page.getByLabel('Datesselected').getByRole('button').first().click() await page.getByRole('combobox', { name: 'Select month' }).selectOption('12') await page.getByRole('combobox', { name: 'Select year' }).selectOption('2022') diff --git a/src-ui/e2e/document-list/requests/api-document-list3.har b/src-ui/e2e/document-list/requests/api-document-list3.har index 6d5d1808f..291915a65 100644 --- a/src-ui/e2e/document-list/requests/api-document-list3.har +++ b/src-ui/e2e/document-list/requests/api-document-list3.har @@ -3687,7 +3687,7 @@ "time": 1.501, "request": { "method": "GET", - "url": "http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&truncate_content=true&created__date__gt=2022-12-11", + "url": "http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&truncate_content=true&created__date__gte=2022-12-11", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [ @@ -3721,7 +3721,7 @@ "value": "true" }, { - "name": "created__date__gt", + "name": "created__date__gte", "value": "2022-12-11" } ], diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 9983e6b55..caab96d4b 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -1167,7 +1167,7 @@ src/app/components/document-list/filter-editor/filter-editor.component.ts - 166 + 170 @@ -3318,48 +3318,114 @@ 93 - - True + + Today + + src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html + 39 + + + src/app/components/common/dates-dropdown/dates-dropdown.component.html + 50 + + + src/app/components/common/dates-dropdown/dates-dropdown.component.html + 76 + + + src/app/components/common/dates-dropdown/dates-dropdown.component.html + 126 + + + src/app/components/common/dates-dropdown/dates-dropdown.component.html + 152 + + + src/app/components/common/input/date/date.component.html + 21 + + + + Close src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html 40 - src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html + src/app/components/common/dates-dropdown/dates-dropdown.component.html + 51 + + + src/app/components/common/dates-dropdown/dates-dropdown.component.html 77 + + src/app/components/common/dates-dropdown/dates-dropdown.component.html + 127 + + + src/app/components/common/dates-dropdown/dates-dropdown.component.html + 153 + + + src/app/components/common/input/date/date.component.html + 22 + + + src/app/components/document-detail/document-detail.component.html + 94 + + + src/app/components/document-detail/document-detail.component.ts + 1375 + + + src/app/guards/dirty-saved-view.guard.ts + 37 + + + + True src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 83 + 47 + + + src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html + 84 + + + src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html + 90 False src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 41 + 48 src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 78 + 85 src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 84 + 91 Search docs... src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 100 + 107 Any src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 132 + 139 src/app/components/common/filterable-dropdown/filterable-dropdown.component.html @@ -3370,7 +3436,7 @@ All src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 134 + 141 src/app/components/common/filterable-dropdown/filterable-dropdown.component.html @@ -3397,21 +3463,21 @@ Not src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 137 + 144 Add query src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 156 + 163 Add expression src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 159 + 166 @@ -3422,36 +3488,36 @@ src/app/components/common/dates-dropdown/dates-dropdown.component.html - 89 + 101 - - After + + From src/app/components/common/dates-dropdown/dates-dropdown.component.html 42 src/app/components/common/dates-dropdown/dates-dropdown.component.html - 106 + 118 - - Before + + To src/app/components/common/dates-dropdown/dates-dropdown.component.html - 62 + 68 src/app/components/common/dates-dropdown/dates-dropdown.component.html - 126 + 144 Added src/app/components/common/dates-dropdown/dates-dropdown.component.html - 74 + 86 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts @@ -3470,41 +3536,33 @@ 93 - - Last 7 days + + Within 1 week src/app/components/common/dates-dropdown/dates-dropdown.component.ts 67 - - Last month + + Within 1 month src/app/components/common/dates-dropdown/dates-dropdown.component.ts 72 - - src/app/pipes/custom-date.pipe.ts - 19 - - - Last 3 months + + Within 3 months src/app/components/common/dates-dropdown/dates-dropdown.component.ts 77 - - Last year + + Within 1 year src/app/components/common/dates-dropdown/dates-dropdown.component.ts 82 - - src/app/pipes/custom-date.pipe.ts - 14 - Matching algorithm @@ -5071,14 +5129,14 @@ Invalid date. src/app/components/common/input/date/date.component.html - 25 + 31 Suggestions: src/app/components/common/input/date/date.component.html - 31 + 37 src/app/components/common/input/select/select.component.html @@ -5093,7 +5151,7 @@ Filter documents with this src/app/components/common/input/date/date.component.ts - 121 + 123 src/app/components/common/input/select/select.component.ts @@ -6257,21 +6315,6 @@ 70 - - Close - - src/app/components/document-detail/document-detail.component.html - 94 - - - src/app/components/document-detail/document-detail.component.ts - 1375 - - - src/app/guards/dirty-saved-view.guard.ts - 37 - - Previous @@ -6298,7 +6341,7 @@ src/app/components/document-list/filter-editor/filter-editor.component.ts - 154 + 158 src/app/data/document.ts @@ -6926,7 +6969,7 @@ src/app/components/document-list/filter-editor/filter-editor.component.ts - 162 + 166 @@ -7528,7 +7571,7 @@ src/app/components/document-list/filter-editor/filter-editor.component.ts - 159 + 163 src/app/data/document.ts @@ -7717,147 +7760,147 @@ Title & content src/app/components/document-list/filter-editor/filter-editor.component.ts - 157 + 161 More like src/app/components/document-list/filter-editor/filter-editor.component.ts - 172 + 176 equals src/app/components/document-list/filter-editor/filter-editor.component.ts - 178 + 182 is empty src/app/components/document-list/filter-editor/filter-editor.component.ts - 182 + 186 is not empty src/app/components/document-list/filter-editor/filter-editor.component.ts - 186 + 190 greater than src/app/components/document-list/filter-editor/filter-editor.component.ts - 190 + 194 less than src/app/components/document-list/filter-editor/filter-editor.component.ts - 194 + 198 Correspondent: src/app/components/document-list/filter-editor/filter-editor.component.ts - 226,228 + 230,232 Without correspondent src/app/components/document-list/filter-editor/filter-editor.component.ts - 230 + 234 Document type: src/app/components/document-list/filter-editor/filter-editor.component.ts - 236,238 + 240,242 Without document type src/app/components/document-list/filter-editor/filter-editor.component.ts - 240 + 244 Storage path: src/app/components/document-list/filter-editor/filter-editor.component.ts - 246,248 + 250,252 Without storage path src/app/components/document-list/filter-editor/filter-editor.component.ts - 250 + 254 Tag: src/app/components/document-list/filter-editor/filter-editor.component.ts - 254,256 + 258,260 Without any tag src/app/components/document-list/filter-editor/filter-editor.component.ts - 260 + 264 Custom fields query src/app/components/document-list/filter-editor/filter-editor.component.ts - 264 + 268 Title: src/app/components/document-list/filter-editor/filter-editor.component.ts - 267 + 271 ASN: src/app/components/document-list/filter-editor/filter-editor.component.ts - 270 + 274 Owner: src/app/components/document-list/filter-editor/filter-editor.component.ts - 273 + 277 Owner not in: src/app/components/document-list/filter-editor/filter-editor.component.ts - 276 + 280 Without an owner src/app/components/document-list/filter-editor/filter-editor.component.ts - 279 + 283 @@ -9149,6 +9192,13 @@ 36 + + Last year + + src/app/pipes/custom-date.pipe.ts + 14 + + %s years ago @@ -9156,6 +9206,13 @@ 15 + + Last month + + src/app/pipes/custom-date.pipe.ts + 19 + + %s months ago diff --git a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html index 2f119b074..742dd8e8a 100644 --- a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html +++ b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html @@ -29,10 +29,17 @@ + #d="ngbDatepicker" + [footerTemplate]="datePickerFooterTemplate" /> + +
+ + +
+
} @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Float || getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Integer) { } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Boolean) { diff --git a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.scss b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.scss index f38bb4002..2f9e2c45e 100644 --- a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.scss +++ b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.scss @@ -41,3 +41,9 @@ min-width: 140px; } } + +.btn-group-xs { + > .btn { + border-radius: 0.15rem; + } +} diff --git a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts index 0fa7fe536..324fd27a5 100644 --- a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts +++ b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts @@ -241,6 +241,8 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm customFields: CustomField[] = [] + public readonly today: string = new Date().toISOString().split('T')[0] + constructor(protected customFieldsService: CustomFieldsService) { super() this.selectionModel = new CustomFieldQueriesModel() diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html index dcab4606d..c3ff61ba8 100644 --- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html +++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.html @@ -1,5 +1,5 @@
- + +
+ + +
+
@@ -95,40 +107,52 @@ diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.scss b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.scss index e101a131d..ebd34b29a 100644 --- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.scss +++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.scss @@ -41,3 +41,9 @@ } } } + +.btn-group-xs { + > .btn { + border-radius: 0.15rem; + } +} diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts index 10762264a..1f6ee909e 100644 --- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts @@ -61,7 +61,7 @@ describe('DatesDropdownComponent', () => { it('should support date input, emit change', fakeAsync(() => { let result: string - component.createdDateAfterChange.subscribe((date) => (result = date)) + component.createdDateFromChange.subscribe((date) => (result = date)) const input: HTMLInputElement = fixture.nativeElement.querySelector('input') input.value = '5/30/2023' input.dispatchEvent(new Event('change')) @@ -83,68 +83,68 @@ describe('DatesDropdownComponent', () => { let result: DateSelection component.datesSet.subscribe((date) => (result = date)) component.setCreatedRelativeDate(null) - component.setCreatedRelativeDate(RelativeDate.LAST_7_DAYS) + component.setCreatedRelativeDate(RelativeDate.WITHIN_1_WEEK) component.setAddedRelativeDate(null) - component.setAddedRelativeDate(RelativeDate.LAST_7_DAYS) + component.setAddedRelativeDate(RelativeDate.WITHIN_1_WEEK) tick(500) expect(result).toEqual({ - createdAfter: null, - createdBefore: null, - createdRelativeDateID: RelativeDate.LAST_7_DAYS, - addedAfter: null, - addedBefore: null, - addedRelativeDateID: RelativeDate.LAST_7_DAYS, + createdFrom: null, + createdTo: null, + createdRelativeDateID: RelativeDate.WITHIN_1_WEEK, + addedFrom: null, + addedTo: null, + addedRelativeDateID: RelativeDate.WITHIN_1_WEEK, }) })) it('should support report if active', () => { - component.createdRelativeDate = RelativeDate.LAST_7_DAYS + component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK expect(component.isActive).toBeTruthy() component.createdRelativeDate = null - component.createdDateAfter = '2023-05-30' + component.createdDateFrom = '2023-05-30' expect(component.isActive).toBeTruthy() - component.createdDateAfter = null - component.createdDateBefore = '2023-05-30' + component.createdDateFrom = null + component.createdDateTo = '2023-05-30' expect(component.isActive).toBeTruthy() - component.createdDateBefore = null + component.createdDateTo = null - component.addedRelativeDate = RelativeDate.LAST_7_DAYS + component.addedRelativeDate = RelativeDate.WITHIN_1_WEEK expect(component.isActive).toBeTruthy() component.addedRelativeDate = null - component.addedDateAfter = '2023-05-30' + component.addedDateFrom = '2023-05-30' expect(component.isActive).toBeTruthy() - component.addedDateAfter = null - component.addedDateBefore = '2023-05-30' + component.addedDateFrom = null + component.addedDateTo = '2023-05-30' expect(component.isActive).toBeTruthy() - component.addedDateBefore = null + component.addedDateTo = null expect(component.isActive).toBeFalsy() }) it('should support reset', () => { - component.createdDateAfter = '2023-05-30' + component.createdDateFrom = '2023-05-30' component.reset() - expect(component.createdDateAfter).toBeNull() + expect(component.createdDateFrom).toBeNull() }) - it('should support clearAfter', () => { - component.createdDateAfter = '2023-05-30' - component.clearCreatedAfter() - expect(component.createdDateAfter).toBeNull() + it('should support clearFrom', () => { + component.createdDateFrom = '2023-05-30' + component.clearCreatedFrom() + expect(component.createdDateFrom).toBeNull() - component.addedDateAfter = '2023-05-30' - component.clearAddedAfter() - expect(component.addedDateAfter).toBeNull() + component.addedDateFrom = '2023-05-30' + component.clearAddedFrom() + expect(component.addedDateFrom).toBeNull() }) - it('should support clearBefore', () => { - component.createdDateBefore = '2023-05-30' - component.clearCreatedBefore() - expect(component.createdDateBefore).toBeNull() + it('should support clearTo', () => { + component.createdDateTo = '2023-05-30' + component.clearCreatedTo() + expect(component.createdDateTo).toBeNull() - component.addedDateBefore = '2023-05-30' - component.clearAddedBefore() - expect(component.addedDateBefore).toBeNull() + component.addedDateTo = '2023-05-30' + component.clearAddedTo() + expect(component.addedDateTo).toBeNull() }) it('should limit keyboard events', () => { diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts index 356ba510a..e7d506d18 100644 --- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts +++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.ts @@ -23,19 +23,19 @@ import { popperOptionsReenablePreventOverflow } from 'src/app/utils/popper-optio import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component' export interface DateSelection { - createdBefore?: string - createdAfter?: string + createdTo?: string + createdFrom?: string createdRelativeDateID?: number - addedBefore?: string - addedAfter?: string + addedTo?: string + addedFrom?: string addedRelativeDateID?: number } export enum RelativeDate { - LAST_7_DAYS = 0, - LAST_MONTH = 1, - LAST_3_MONTHS = 2, - LAST_YEAR = 3, + WITHIN_1_WEEK = 0, + WITHIN_1_MONTH = 1, + WITHIN_3_MONTHS = 2, + WITHIN_1_YEAR = 3, } @Component({ @@ -63,23 +63,23 @@ export class DatesDropdownComponent implements OnInit, OnDestroy { relativeDates = [ { - id: RelativeDate.LAST_7_DAYS, - name: $localize`Last 7 days`, + id: RelativeDate.WITHIN_1_WEEK, + name: $localize`Within 1 week`, date: new Date().setDate(new Date().getDate() - 7), }, { - id: RelativeDate.LAST_MONTH, - name: $localize`Last month`, + id: RelativeDate.WITHIN_1_MONTH, + name: $localize`Within 1 month`, date: new Date().setMonth(new Date().getMonth() - 1), }, { - id: RelativeDate.LAST_3_MONTHS, - name: $localize`Last 3 months`, + id: RelativeDate.WITHIN_3_MONTHS, + name: $localize`Within 3 months`, date: new Date().setMonth(new Date().getMonth() - 3), }, { - id: RelativeDate.LAST_YEAR, - name: $localize`Last year`, + id: RelativeDate.WITHIN_1_YEAR, + name: $localize`Within 1 year`, date: new Date().setFullYear(new Date().getFullYear() - 1), }, ] @@ -88,16 +88,16 @@ export class DatesDropdownComponent implements OnInit, OnDestroy { // created @Input() - createdDateBefore: string + createdDateTo: string @Output() - createdDateBeforeChange = new EventEmitter() + createdDateToChange = new EventEmitter() @Input() - createdDateAfter: string + createdDateFrom: string @Output() - createdDateAfterChange = new EventEmitter() + createdDateFromChange = new EventEmitter() @Input() createdRelativeDate: RelativeDate @@ -107,16 +107,16 @@ export class DatesDropdownComponent implements OnInit, OnDestroy { // added @Input() - addedDateBefore: string + addedDateTo: string @Output() - addedDateBeforeChange = new EventEmitter() + addedDateToChange = new EventEmitter() @Input() - addedDateAfter: string + addedDateFrom: string @Output() - addedDateAfterChange = new EventEmitter() + addedDateFromChange = new EventEmitter() @Input() addedRelativeDate: RelativeDate @@ -133,14 +133,16 @@ export class DatesDropdownComponent implements OnInit, OnDestroy { @Input() disabled: boolean = false + public readonly today: string = new Date().toISOString().split('T')[0] + get isActive(): boolean { return ( this.createdRelativeDate !== null || - this.createdDateAfter?.length > 0 || - this.createdDateBefore?.length > 0 || + this.createdDateFrom?.length > 0 || + this.createdDateTo?.length > 0 || this.addedRelativeDate !== null || - this.addedDateAfter?.length > 0 || - this.addedDateBefore?.length > 0 + this.addedDateFrom?.length > 0 || + this.addedDateTo?.length > 0 ) } @@ -161,42 +163,42 @@ export class DatesDropdownComponent implements OnInit, OnDestroy { } reset() { - this.createdDateBefore = null - this.createdDateAfter = null + this.createdDateTo = null + this.createdDateFrom = null this.createdRelativeDate = null - this.addedDateBefore = null - this.addedDateAfter = null + this.addedDateTo = null + this.addedDateFrom = null this.addedRelativeDate = null this.onChange() } setCreatedRelativeDate(rd: RelativeDate) { - this.createdDateBefore = null - this.createdDateAfter = null + this.createdDateTo = null + this.createdDateFrom = null this.createdRelativeDate = this.createdRelativeDate == rd ? null : rd this.onChange() } setAddedRelativeDate(rd: RelativeDate) { - this.addedDateBefore = null - this.addedDateAfter = null + this.addedDateTo = null + this.addedDateFrom = null this.addedRelativeDate = this.addedRelativeDate == rd ? null : rd this.onChange() } onChange() { - this.createdDateBeforeChange.emit(this.createdDateBefore) - this.createdDateAfterChange.emit(this.createdDateAfter) + this.createdDateToChange.emit(this.createdDateTo) + this.createdDateFromChange.emit(this.createdDateFrom) this.createdRelativeDateChange.emit(this.createdRelativeDate) - this.addedDateBeforeChange.emit(this.addedDateBefore) - this.addedDateAfterChange.emit(this.addedDateAfter) + this.addedDateToChange.emit(this.addedDateTo) + this.addedDateFromChange.emit(this.addedDateFrom) this.addedRelativeDateChange.emit(this.addedRelativeDate) this.datesSet.emit({ - createdAfter: this.createdDateAfter, - createdBefore: this.createdDateBefore, + createdFrom: this.createdDateFrom, + createdTo: this.createdDateTo, createdRelativeDateID: this.createdRelativeDate, - addedAfter: this.addedDateAfter, - addedBefore: this.addedDateBefore, + addedFrom: this.addedDateFrom, + addedTo: this.addedDateTo, addedRelativeDateID: this.addedRelativeDate, }) } @@ -205,30 +207,30 @@ export class DatesDropdownComponent implements OnInit, OnDestroy { this.createdRelativeDate = null this.addedRelativeDate = null this.datesSetDebounce$.next({ - createdAfter: this.createdDateAfter, - createdBefore: this.createdDateBefore, - addedAfter: this.addedDateAfter, - addedBefore: this.addedDateBefore, + createdAfter: this.createdDateFrom, + createdBefore: this.createdDateTo, + addedAfter: this.addedDateFrom, + addedBefore: this.addedDateTo, }) } - clearCreatedBefore() { - this.createdDateBefore = null + clearCreatedTo() { + this.createdDateTo = null this.onChange() } - clearCreatedAfter() { - this.createdDateAfter = null + clearCreatedFrom() { + this.createdDateFrom = null this.onChange() } - clearAddedBefore() { - this.addedDateBefore = null + clearAddedTo() { + this.addedDateTo = null this.onChange() } - clearAddedAfter() { - this.addedDateAfter = null + clearAddedFrom() { + this.addedDateFrom = null this.onChange() } diff --git a/src-ui/src/app/components/common/input/date/date.component.html b/src-ui/src/app/components/common/input/date/date.component.html index 8f386e2c8..3221677fc 100644 --- a/src-ui/src/app/components/common/input/date/date.component.html +++ b/src-ui/src/app/components/common/input/date/date.component.html @@ -12,10 +12,16 @@
+ name="dp" [(ngModel)]="value" ngbDatepicker #datePicker="ngbDatepicker" #datePickerContent="ngModel" [disabled]="disabled" [footerTemplate]="datePickerFooterTemplate"> + +
+ + +
+
@if (showFilter) {
    + diff --git a/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.scss b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.scss new file mode 100644 index 000000000..2332e710d --- /dev/null +++ b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.scss @@ -0,0 +1,22 @@ +.dropdown-menu { + width: var(--pngx-toast-max-width); +} + +.dropdown-menu .scroll-list { + max-height: 500px; + overflow-y: auto; +} + +.dropdown-toggle::after { + display: none; +} + +.dropdown-item { + white-space: initial; +} + +@media screen and (max-width: 400px) { + :host ::ng-deep .dropdown-menu-end { + right: -3rem; + } +} diff --git a/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.spec.ts b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.spec.ts new file mode 100644 index 000000000..33b948f30 --- /dev/null +++ b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.spec.ts @@ -0,0 +1,112 @@ +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' +import { provideHttpClientTesting } from '@angular/common/http/testing' +import { + ComponentFixture, + TestBed, + discardPeriodicTasks, + fakeAsync, + flush, +} from '@angular/core/testing' +import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { Subject } from 'rxjs' +import { Toast, ToastService } from 'src/app/services/toast.service' +import { ToastsDropdownComponent } from './toasts-dropdown.component' + +const toasts = [ + { + id: 'abc-123', + content: 'foo bar', + delay: 5000, + }, + { + id: 'def-123', + content: 'Error 1 content', + delay: 5000, + error: 'Error 1 string', + }, + { + id: 'ghi-123', + content: 'Error 2 content', + delay: 5000, + error: { + url: 'https://example.com', + status: 500, + statusText: 'Internal Server Error', + message: 'Internal server error 500 message', + error: { detail: 'Error 2 message details' }, + }, + }, +] + +describe('ToastsDropdownComponent', () => { + let component: ToastsDropdownComponent + let fixture: ComponentFixture + let toastService: ToastService + let toastsSubject: Subject = new Subject() + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [ + ToastsDropdownComponent, + NgxBootstrapIconsModule.pick(allIcons), + ], + providers: [ + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], + }).compileComponents() + + fixture = TestBed.createComponent(ToastsDropdownComponent) + toastService = TestBed.inject(ToastService) + jest.spyOn(toastService, 'getToasts').mockReturnValue(toastsSubject) + + component = fixture.componentInstance + + fixture.detectChanges() + }) + + it('should call getToasts and return toasts', fakeAsync(() => { + const spy = jest.spyOn(toastService, 'getToasts') + + component.ngOnInit() + toastsSubject.next(toasts) + fixture.detectChanges() + + expect(spy).toHaveBeenCalled() + expect(component.toasts).toContainEqual({ + id: 'abc-123', + content: 'foo bar', + delay: 5000, + }) + + component.ngOnDestroy() + flush() + discardPeriodicTasks() + })) + + it('should show a toast', fakeAsync(() => { + component.ngOnInit() + toastsSubject.next(toasts) + fixture.detectChanges() + + expect(fixture.nativeElement.textContent).toContain('foo bar') + + component.ngOnDestroy() + flush() + discardPeriodicTasks() + })) + + it('should toggle suppressPopupToasts', fakeAsync((finish) => { + component.ngOnInit() + fixture.detectChanges() + toastsSubject.next(toasts) + + const spy = jest.spyOn(toastService, 'suppressPopupToasts', 'set') + component.onOpenChange(true) + expect(spy).toHaveBeenCalledWith(true) + + component.ngOnDestroy() + flush() + discardPeriodicTasks() + })) +}) diff --git a/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.ts b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.ts new file mode 100644 index 000000000..c04d758af --- /dev/null +++ b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.ts @@ -0,0 +1,42 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { + NgbDropdownModule, + NgbProgressbarModule, +} from '@ng-bootstrap/ng-bootstrap' +import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' +import { Subscription } from 'rxjs' +import { Toast, ToastService } from 'src/app/services/toast.service' +import { ToastComponent } from '../../common/toast/toast.component' + +@Component({ + selector: 'pngx-toasts-dropdown', + templateUrl: './toasts-dropdown.component.html', + styleUrls: ['./toasts-dropdown.component.scss'], + imports: [ + ToastComponent, + NgbDropdownModule, + NgbProgressbarModule, + NgxBootstrapIconsModule, + ], +}) +export class ToastsDropdownComponent implements OnInit, OnDestroy { + constructor(public toastService: ToastService) {} + + private subscription: Subscription + + public toasts: Toast[] = [] + + ngOnDestroy(): void { + this.subscription?.unsubscribe() + } + + ngOnInit(): void { + this.subscription = this.toastService.getToasts().subscribe((toasts) => { + this.toasts = [...toasts] + }) + } + + onOpenChange(open: boolean): void { + this.toastService.suppressPopupToasts = open + } +} diff --git a/src-ui/src/app/components/common/toast/toast.component.html b/src-ui/src/app/components/common/toast/toast.component.html new file mode 100644 index 000000000..ede75ddea --- /dev/null +++ b/src-ui/src/app/components/common/toast/toast.component.html @@ -0,0 +1,56 @@ + + @if (autohide) { + + {{ toast.delayRemaining / 1000 | number: '1.0-0' }} seconds + } +
    + @if (!toast.error) { + + } + @if (toast.error) { + + } +
    +

    {{toast.content}}

    + @if (toast.error) { +
    +
    + @if (isDetailedError(toast.error)) { +
    +
    URL
    +
    {{ toast.error.url }}
    +
    Status
    +
    {{ toast.error.status }} {{ toast.error.statusText }}
    +
    Error
    +
    {{ getErrorText(toast.error) }}
    +
    + } +
    +
    + +
    +
    +
    +
    + } + @if (toast.action) { +

    + } +
    + +
    +
    diff --git a/src-ui/src/app/components/common/toast/toast.component.scss b/src-ui/src/app/components/common/toast/toast.component.scss new file mode 100644 index 000000000..3783445de --- /dev/null +++ b/src-ui/src/app/components/common/toast/toast.component.scss @@ -0,0 +1,20 @@ +::ng-deep .toast-body { + position: relative; +} + +::ng-deep .toast.error { + border-color: hsla(350, 79%, 40%, 0.4); // bg-danger +} + +::ng-deep .toast.error .toast-body { + background-color: hsla(350, 79%, 40%, 0.8); // bg-danger + border-top-left-radius: inherit; + border-top-right-radius: inherit; + border-bottom-left-radius: inherit; + border-bottom-right-radius: inherit; +} + +.progress { + background-color: var(--pngx-primary); + opacity: .07; +} diff --git a/src-ui/src/app/components/common/toast/toast.component.spec.ts b/src-ui/src/app/components/common/toast/toast.component.spec.ts new file mode 100644 index 000000000..c5d52a28f --- /dev/null +++ b/src-ui/src/app/components/common/toast/toast.component.spec.ts @@ -0,0 +1,104 @@ +import { + ComponentFixture, + discardPeriodicTasks, + fakeAsync, + flush, + TestBed, + tick, +} from '@angular/core/testing' + +import { Clipboard } from '@angular/cdk/clipboard' +import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' +import { ToastComponent } from './toast.component' + +const toast1 = { + content: 'Error 1 content', + delay: 5000, + error: 'Error 1 string', +} + +const toast2 = { + content: 'Error 2 content', + delay: 5000, + error: { + url: 'https://example.com', + status: 500, + statusText: 'Internal Server Error', + message: 'Internal server error 500 message', + error: { detail: 'Error 2 message details' }, + }, +} + +describe('ToastComponent', () => { + let component: ToastComponent + let fixture: ComponentFixture + let clipboard: Clipboard + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ToastComponent, NgxBootstrapIconsModule.pick(allIcons)], + }).compileComponents() + + fixture = TestBed.createComponent(ToastComponent) + clipboard = TestBed.inject(Clipboard) + component = fixture.componentInstance + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) + + it('should countdown toast', fakeAsync(() => { + component.toast = toast2 + fixture.detectChanges() + component.onShown(toast2) + tick(5000) + expect(component.toast.delayRemaining).toEqual(0) + flush() + discardPeriodicTasks() + })) + + it('should show an error if given with toast', fakeAsync(() => { + component.toast = toast1 + fixture.detectChanges() + + expect(fixture.nativeElement.querySelector('details')).not.toBeNull() + expect(fixture.nativeElement.textContent).toContain('Error 1 content') + + flush() + discardPeriodicTasks() + })) + + it('should show error details, support copy', fakeAsync(() => { + component.toast = toast2 + fixture.detectChanges() + + expect(fixture.nativeElement.querySelector('details')).not.toBeNull() + expect(fixture.nativeElement.textContent).toContain( + 'Error 2 message details' + ) + + const copySpy = jest.spyOn(clipboard, 'copy') + component.copyError(toast2.error) + expect(copySpy).toHaveBeenCalled() + + flush() + discardPeriodicTasks() + })) + + it('should parse error text, add ellipsis', () => { + expect(component.getErrorText(toast2.error)).toEqual( + 'Error 2 message details' + ) + expect(component.getErrorText({ error: 'Error string no detail' })).toEqual( + 'Error string no detail' + ) + expect(component.getErrorText('Error string')).toEqual('') + expect( + component.getErrorText({ error: { message: 'foo error bar' } }) + ).toContain('{"message":"foo error bar"}') + expect( + component.getErrorText({ error: new Array(205).join('a') }) + ).toContain('...') + }) +}) diff --git a/src-ui/src/app/components/common/toast/toast.component.ts b/src-ui/src/app/components/common/toast/toast.component.ts new file mode 100644 index 000000000..5ebfdbe82 --- /dev/null +++ b/src-ui/src/app/components/common/toast/toast.component.ts @@ -0,0 +1,76 @@ +import { Clipboard } from '@angular/cdk/clipboard' +import { DecimalPipe } from '@angular/common' +import { Component, EventEmitter, Input, Output } from '@angular/core' +import { + NgbProgressbarModule, + NgbToastModule, +} from '@ng-bootstrap/ng-bootstrap' +import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' +import { interval, take } from 'rxjs' +import { Toast } from 'src/app/services/toast.service' + +@Component({ + selector: 'pngx-toast', + imports: [ + DecimalPipe, + NgbToastModule, + NgbProgressbarModule, + NgxBootstrapIconsModule, + ], + templateUrl: './toast.component.html', + styleUrl: './toast.component.scss', +}) +export class ToastComponent { + @Input() toast: Toast + + @Input() autohide: boolean = true + + @Output() hidden: EventEmitter = new EventEmitter() + + @Output() close: EventEmitter = new EventEmitter() + + public copied: boolean = false + + constructor(private clipboard: Clipboard) {} + + onShown(toast: Toast) { + if (!this.autohide) return + + const refreshInterval = 150 + const delay = toast.delay - 500 // for fade animation + + interval(refreshInterval) + .pipe(take(Math.round(delay / refreshInterval))) + .subscribe((count) => { + toast.delayRemaining = Math.max( + 0, + delay - refreshInterval * (count + 1) + ) + }) + } + + public isDetailedError(error: any): boolean { + return ( + typeof error === 'object' && + 'status' in error && + 'statusText' in error && + 'url' in error && + 'message' in error && + 'error' in error + ) + } + + public copyError(error: any) { + this.clipboard.copy(JSON.stringify(error)) + this.copied = true + setTimeout(() => { + this.copied = false + }, 3000) + } + + getErrorText(error: any) { + let text: string = error.error?.detail ?? error.error ?? '' + if (typeof text === 'object') text = JSON.stringify(text) + return `${text.slice(0, 200)}${text.length > 200 ? '...' : ''}` + } +} diff --git a/src-ui/src/app/components/common/toasts/toasts.component.html b/src-ui/src/app/components/common/toasts/toasts.component.html index 36623161b..2178a2023 100644 --- a/src-ui/src/app/components/common/toasts/toasts.component.html +++ b/src-ui/src/app/components/common/toasts/toasts.component.html @@ -1,55 +1,3 @@ -@for (toast of toasts; track toast) { - - - {{ toast.delayRemaining / 1000 | number: '1.0-0' }} seconds -
    - @if (!toast.error) { - - } - @if (toast.error) { - - } -
    -

    {{toast.content}}

    - @if (toast.error) { -
    -
    - @if (isDetailedError(toast.error)) { -
    -
    URL
    -
    {{ toast.error.url }}
    -
    Status
    -
    {{ toast.error.status }} {{ toast.error.statusText }}
    -
    Error
    -
    {{ getErrorText(toast.error) }}
    -
    - } -
    -
    - -
    -
    -
    -
    - } - @if (toast.action) { -

    - } -
    - -
    -
    +@for (toast of toasts; track toast.id) { + } diff --git a/src-ui/src/app/components/common/toasts/toasts.component.scss b/src-ui/src/app/components/common/toasts/toasts.component.scss index 463f96495..e0a069dda 100644 --- a/src-ui/src/app/components/common/toasts/toasts.component.scss +++ b/src-ui/src/app/components/common/toasts/toasts.component.scss @@ -1,7 +1,7 @@ :host { position: fixed; top: 0; - right: 0; + right: calc(50% - (var(--pngx-toast-max-width) / 2)); margin: 0.3em; z-index: 1200; } @@ -9,24 +9,3 @@ .toast:not(.show) { display: block; // this corrects an ng-bootstrap bug that prevented animations } - -::ng-deep .toast-body { - position: relative; -} - -::ng-deep .toast.error { - border-color: hsla(350, 79%, 40%, 0.4); // bg-danger -} - -::ng-deep .toast.error .toast-body { - background-color: hsla(350, 79%, 40%, 0.8); // bg-danger - border-top-left-radius: inherit; - border-top-right-radius: inherit; - border-bottom-left-radius: inherit; - border-bottom-right-radius: inherit; -} - -.progress { - background-color: var(--pngx-primary); - opacity: .07; -} diff --git a/src-ui/src/app/components/common/toasts/toasts.component.spec.ts b/src-ui/src/app/components/common/toasts/toasts.component.spec.ts index 449396134..bbea04c9c 100644 --- a/src-ui/src/app/components/common/toasts/toasts.component.spec.ts +++ b/src-ui/src/app/components/common/toasts/toasts.component.spec.ts @@ -1,58 +1,33 @@ -import { Clipboard } from '@angular/cdk/clipboard' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - discardPeriodicTasks, - fakeAsync, - flush, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' -import { of } from 'rxjs' -import { ToastService } from 'src/app/services/toast.service' +import { Subject } from 'rxjs' +import { Toast, ToastService } from 'src/app/services/toast.service' import { ToastsComponent } from './toasts.component' -const toasts = [ - { - content: 'foo bar', - delay: 5000, +const toast = { + content: 'Error 2 content', + delay: 5000, + error: { + url: 'https://example.com', + status: 500, + statusText: 'Internal Server Error', + message: 'Internal server error 500 message', + error: { detail: 'Error 2 message details' }, }, - { - content: 'Error 1 content', - delay: 5000, - error: 'Error 1 string', - }, - { - content: 'Error 2 content', - delay: 5000, - error: { - url: 'https://example.com', - status: 500, - statusText: 'Internal Server Error', - message: 'Internal server error 500 message', - error: { detail: 'Error 2 message details' }, - }, - }, -] +} describe('ToastsComponent', () => { let component: ToastsComponent let fixture: ComponentFixture let toastService: ToastService - let clipboard: Clipboard + let toastSubject: Subject = new Subject() beforeEach(async () => { TestBed.configureTestingModule({ imports: [ToastsComponent, NgxBootstrapIconsModule.pick(allIcons)], providers: [ - { - provide: ToastService, - useValue: { - getToasts: () => of(toasts), - }, - }, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), ], @@ -60,95 +35,37 @@ describe('ToastsComponent', () => { fixture = TestBed.createComponent(ToastsComponent) toastService = TestBed.inject(ToastService) - clipboard = TestBed.inject(Clipboard) + jest.replaceProperty(toastService, 'showToast', toastSubject) component = fixture.componentInstance fixture.detectChanges() }) - it('should call getToasts and return toasts', fakeAsync(() => { - const spy = jest.spyOn(toastService, 'getToasts') + it('should create', () => { + expect(component).toBeTruthy() + }) - component.ngOnInit() - fixture.detectChanges() + it('should close toast', () => { + component.toasts = [toast] + const closeToastSpy = jest.spyOn(toastService, 'closeToast') + component.closeToast() + expect(component.toasts).toEqual([]) + expect(closeToastSpy).toHaveBeenCalledWith(toast) + }) - expect(spy).toHaveBeenCalled() - expect(component.toasts).toContainEqual({ - content: 'foo bar', - delay: 5000, - }) - - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) - - it('should show a toast', fakeAsync(() => { - component.ngOnInit() - fixture.detectChanges() - - expect(fixture.nativeElement.textContent).toContain('foo bar') - - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) - - it('should countdown toast', fakeAsync(() => { - component.ngOnInit() - fixture.detectChanges() - component.onShow(toasts[0]) - tick(5000) - expect(component.toasts[0].delayRemaining).toEqual(0) - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) - - it('should show an error if given with toast', fakeAsync(() => { - component.ngOnInit() - fixture.detectChanges() - - expect(fixture.nativeElement.querySelector('details')).not.toBeNull() - expect(fixture.nativeElement.textContent).toContain('Error 1 content') - - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) - - it('should show error details, support copy', fakeAsync(() => { - component.ngOnInit() - fixture.detectChanges() - - expect(fixture.nativeElement.querySelector('details')).not.toBeNull() - expect(fixture.nativeElement.textContent).toContain( - 'Error 2 message details' + it('should unsubscribe', () => { + const unsubscribeSpy = jest.spyOn( + (component as any).subscription, + 'unsubscribe' ) - - const copySpy = jest.spyOn(clipboard, 'copy') - component.copyError(toasts[2].error) - expect(copySpy).toHaveBeenCalled() - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) + expect(unsubscribeSpy).toHaveBeenCalled() + }) - it('should parse error text, add ellipsis', () => { - expect(component.getErrorText(toasts[2].error)).toEqual( - 'Error 2 message details' - ) - expect(component.getErrorText({ error: 'Error string no detail' })).toEqual( - 'Error string no detail' - ) - expect(component.getErrorText('Error string')).toEqual('') - expect( - component.getErrorText({ error: { message: 'foo error bar' } }) - ).toContain('{"message":"foo error bar"}') - expect( - component.getErrorText({ error: new Array(205).join('a') }) - ).toContain('...') + it('should subscribe to toastService', () => { + component.ngOnInit() + toastSubject.next(toast) + expect(component.toasts).toEqual([toast]) }) }) diff --git a/src-ui/src/app/components/common/toasts/toasts.component.ts b/src-ui/src/app/components/common/toasts/toasts.component.ts index bb791de11..53b6e1895 100644 --- a/src-ui/src/app/components/common/toasts/toasts.component.ts +++ b/src-ui/src/app/components/common/toasts/toasts.component.ts @@ -1,92 +1,43 @@ -import { Clipboard } from '@angular/cdk/clipboard' -import { DecimalPipe } from '@angular/common' import { Component, OnDestroy, OnInit } from '@angular/core' import { + NgbAccordionModule, NgbProgressbarModule, - NgbToastModule, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { Subscription, interval, take } from 'rxjs' +import { Subscription } from 'rxjs' import { Toast, ToastService } from 'src/app/services/toast.service' +import { ToastComponent } from '../toast/toast.component' @Component({ selector: 'pngx-toasts', templateUrl: './toasts.component.html', styleUrls: ['./toasts.component.scss'], imports: [ - DecimalPipe, - NgbToastModule, + ToastComponent, + NgbAccordionModule, NgbProgressbarModule, NgxBootstrapIconsModule, ], }) export class ToastsComponent implements OnInit, OnDestroy { - constructor( - public toastService: ToastService, - private clipboard: Clipboard - ) {} + constructor(public toastService: ToastService) {} private subscription: Subscription - public toasts: Toast[] = [] - - public copied: boolean = false - - public seconds: number = 0 + public toasts: Toast[] = [] // array to force change detection ngOnDestroy(): void { this.subscription?.unsubscribe() } ngOnInit(): void { - this.subscription = this.toastService.getToasts().subscribe((toasts) => { - this.toasts = toasts - this.toasts.forEach((t) => { - if (typeof t.error === 'string') { - try { - t.error = JSON.parse(t.error) - } catch (e) {} - } - }) + this.subscription = this.toastService.showToast.subscribe((toast) => { + this.toasts = toast ? [toast] : [] }) } - onShow(toast: Toast) { - const refreshInterval = 150 - const delay = toast.delay - 500 // for fade animation - - interval(refreshInterval) - .pipe(take(delay / refreshInterval)) - .subscribe((count) => { - toast.delayRemaining = Math.max( - 0, - delay - refreshInterval * (count + 1) - ) - }) - } - - public isDetailedError(error: any): boolean { - return ( - typeof error === 'object' && - 'status' in error && - 'statusText' in error && - 'url' in error && - 'message' in error && - 'error' in error - ) - } - - public copyError(error: any) { - this.clipboard.copy(JSON.stringify(error)) - this.copied = true - setTimeout(() => { - this.copied = false - }, 3000) - } - - getErrorText(error: any) { - let text: string = error.error?.detail ?? error.error ?? '' - if (typeof text === 'object') text = JSON.stringify(text) - return `${text.slice(0, 200)}${text.length > 200 ? '...' : ''}` + closeToast() { + this.toastService.closeToast(this.toasts[0]) + this.toasts = [] } } diff --git a/src-ui/src/app/services/toast.service.spec.ts b/src-ui/src/app/services/toast.service.spec.ts index 274ea9db6..ce50b165e 100644 --- a/src-ui/src/app/services/toast.service.spec.ts +++ b/src-ui/src/app/services/toast.service.spec.ts @@ -25,6 +25,33 @@ describe('ToastService', () => { }) }) + it('adds a unique id to toast on show', () => { + const toast = { + title: 'Title', + content: 'content', + delay: 5000, + } + toastService.show(toast) + + toastService.getToasts().subscribe((toasts) => { + expect(toasts[0].id).toBeDefined() + }) + }) + + it('parses error string to object on show', () => { + const toast = { + title: 'Title', + content: 'content', + delay: 5000, + error: 'Error string', + } + toastService.show(toast) + + toastService.getToasts().subscribe((toasts) => { + expect(toasts[0].error).toEqual('Error string') + }) + }) + it('creates toasts with defaults on showInfo and showError', () => { toastService.showInfo('Info toast') toastService.showError('Error toast') @@ -54,4 +81,29 @@ describe('ToastService', () => { expect(toasts).toHaveLength(0) }) }) + + it('clears all toasts on clearToasts', () => { + toastService.showInfo('Info toast') + toastService.showError('Error toast') + toastService.clearToasts() + + toastService.getToasts().subscribe((toasts) => { + expect(toasts).toHaveLength(0) + }) + }) + + it('suppresses popup toasts if suppressPopupToasts is true', (finish) => { + toastService.showToast.subscribe((toast) => { + expect(toast).not.toBeNull() + }) + toastService.showInfo('Info toast') + + toastService.showToast.subscribe((toast) => { + expect(toast).toBeNull() + finish() + }) + + toastService.suppressPopupToasts = true + toastService.showInfo('Info toast') + }) }) diff --git a/src-ui/src/app/services/toast.service.ts b/src-ui/src/app/services/toast.service.ts index 16c534b5c..b917bf94b 100644 --- a/src-ui/src/app/services/toast.service.ts +++ b/src-ui/src/app/services/toast.service.ts @@ -1,7 +1,10 @@ import { Injectable } from '@angular/core' import { Subject } from 'rxjs' +import { v4 as uuidv4 } from 'uuid' export interface Toast { + id?: string + content: string delay: number @@ -22,13 +25,32 @@ export interface Toast { }) export class ToastService { constructor() {} + _suppressPopupToasts: boolean + + set suppressPopupToasts(value: boolean) { + this._suppressPopupToasts = value + this.showToast.next(null) + } private toasts: Toast[] = [] private toastsSubject: Subject = new Subject() + public showToast: Subject = new Subject() + show(toast: Toast) { - this.toasts.push(toast) + if (!toast.id) { + toast.id = uuidv4() + } + if (typeof toast.error === 'string') { + try { + toast.error = JSON.parse(toast.error) + } catch (e) {} + } + this.toasts.unshift(toast) + if (!this._suppressPopupToasts) { + this.showToast.next(toast) + } this.toastsSubject.next(this.toasts) } @@ -46,7 +68,7 @@ export class ToastService { } closeToast(toast: Toast) { - let index = this.toasts.findIndex((t) => t == toast) + let index = this.toasts.findIndex((t) => t.id == toast.id) if (index > -1) { this.toasts.splice(index, 1) this.toastsSubject.next(this.toasts) @@ -56,4 +78,10 @@ export class ToastService { getToasts() { return this.toastsSubject } + + clearToasts() { + this.toasts = [] + this.toastsSubject.next(this.toasts) + this.showToast.next(null) + } } diff --git a/src-ui/src/main.ts b/src-ui/src/main.ts index 83aa12dc2..484a77c82 100644 --- a/src-ui/src/main.ts +++ b/src-ui/src/main.ts @@ -34,6 +34,7 @@ import { arrowRightShort, arrowUpRight, asterisk, + bell, bodyText, boxArrowUp, boxArrowUpRight, @@ -235,6 +236,7 @@ const icons = { arrowRightShort, arrowUpRight, asterisk, + bell, braces, bodyText, boxArrowUp, diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss index 1257798b9..589356566 100644 --- a/src-ui/src/styles.scss +++ b/src-ui/src/styles.scss @@ -570,6 +570,10 @@ table.table { color: var(--bs-body-color); } +.toast { + --bs-toast-max-width: var(--pngx-toast-max-width); +} + .alert-primary { --bs-alert-color: var(--bs-primary); --bs-alert-bg: var(--pngx-primary-faded); diff --git a/src-ui/src/theme.scss b/src-ui/src/theme.scss index 9f3c9cbe9..fc8c13d3b 100644 --- a/src-ui/src/theme.scss +++ b/src-ui/src/theme.scss @@ -24,6 +24,10 @@ --pngx-bg-alt2: var(--bs-gray-200); --pngx-bg-disabled: #f7f7f7; --pngx-focus-alpha: 0.3; + --pngx-toast-max-width: 360px; + @media screen and (min-width: 1024px) { + --pngx-toast-max-width: 450px; + } } // Dark text colors allow for maintain contrast with theme color changes From a4999056054d24d6e8086a63a1e1d3e0a6871e35 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:10:19 -0800 Subject: [PATCH 35/37] Fix mistake in process mail error string --- src-ui/messages.xlf | 4 ++-- src-ui/src/app/components/manage/mail/mail.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 220f851c0..b0d6025c7 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -8328,8 +8328,8 @@ 227 - - Error processing mail account "") + + Error processing mail account "" src/app/components/manage/mail/mail.component.ts 232 diff --git a/src-ui/src/app/components/manage/mail/mail.component.ts b/src-ui/src/app/components/manage/mail/mail.component.ts index c97d7e893..8d4222516 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -229,7 +229,7 @@ export class MailComponent }, error: (e) => { this.toastService.showError( - $localize`Error processing mail account "${account.name}")`, + $localize`Error processing mail account "${account.name}"`, e ) }, From b274665e2103b216407c25b7d59df993d44c6bdf Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:24:23 -0800 Subject: [PATCH 36/37] Tweak: add name to management toasts --- src-ui/messages.xlf | 4 ++-- .../manage/management-list/management-list.component.ts | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index b0d6025c7..1072e89c6 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -8528,8 +8528,8 @@ 183 - - Successfully updated . + + Successfully updated "". src/app/components/manage/management-list/management-list.component.ts 198 diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.ts b/src-ui/src/app/components/manage/management-list/management-list.component.ts index ecb3e2519..7f7721485 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.ts @@ -21,7 +21,6 @@ import { MATCHING_ALGORITHMS, MatchingModel, } from 'src/app/data/matching-model' -import { ObjectWithId } from 'src/app/data/object-with-id' import { ObjectWithPermissions } from 'src/app/data/object-with-permissions' import { SortableDirective, @@ -56,7 +55,7 @@ export interface ManagementListColumn { } @Directive() -export abstract class ManagementListComponent +export abstract class ManagementListComponent extends LoadingComponentWithPermissions implements OnInit, OnDestroy { @@ -195,7 +194,7 @@ export abstract class ManagementListComponent activeModal.componentInstance.succeeded.subscribe(() => { this.reloadData() this.toastService.showInfo( - $localize`Successfully updated ${this.typeName}.` + $localize`Successfully updated ${this.typeName} "${object.name}".` ) }) activeModal.componentInstance.failed.subscribe((e) => { @@ -208,7 +207,7 @@ export abstract class ManagementListComponent abstract getDeleteMessage(object: T) - filterDocuments(object: ObjectWithId) { + filterDocuments(object: MatchingModel) { this.documentListViewService.quickFilter([ { rule_type: this.filterRuleType, value: object.id.toString() }, ]) From e560fa3be03d411dcb83de1d69cda7525df05cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Steinbei=C3=9Fer?= <33968289+gothicVI@users.noreply.github.com> Date: Fri, 7 Feb 2025 18:12:03 +0100 Subject: [PATCH 37/37] Chore: Enable ruff FBT (#8645) --- .ruff.toml | 1 + src/documents/bulk_download.py | 9 ++- src/documents/bulk_edit.py | 3 + src/documents/file_handling.py | 5 +- src/documents/filters.py | 2 +- src/documents/index.py | 6 +- .../commands/convert_mariadb_uuid.py | 2 +- .../management/commands/document_consumer.py | 8 +- .../migrations/1012_fix_archive_files.py | 6 +- src/documents/models.py | 2 +- src/documents/parsers.py | 1 + src/documents/permissions.py | 2 +- src/documents/sanity_checker.py | 2 +- src/documents/signals/handlers.py | 4 + src/documents/tasks.py | 2 +- .../tests/test_api_filter_by_custom_fields.py | 1 + src/documents/tests/test_bulk_edit.py | 9 ++- src/documents/tests/test_consumer.py | 2 +- src/documents/tests/test_delayedquery.py | 6 +- .../tests/test_management_consumer.py | 2 +- .../tests/test_management_exporter.py | 2 +- src/documents/tests/test_matchables.py | 1 + src/documents/views.py | 4 +- src/paperless/views.py | 4 +- src/paperless_mail/mail.py | 35 +++++---- src/paperless_mail/tests/test_mail.py | 76 ++++++++++++++----- src/paperless_tesseract/parsers.py | 1 + 27 files changed, 133 insertions(+), 65 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index d9ca6b321..a29b471c5 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -32,6 +32,7 @@ extend-select = [ "RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf "FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly "PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + "FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt ] ignore = ["DJ001", "SIM105", "RUF012"] diff --git a/src/documents/bulk_download.py b/src/documents/bulk_download.py index 25dfb5a14..5bdc3e74a 100644 --- a/src/documents/bulk_download.py +++ b/src/documents/bulk_download.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: class BulkArchiveStrategy: - def __init__(self, zipf: ZipFile, follow_formatting: bool = False) -> None: + def __init__(self, zipf: ZipFile, *, follow_formatting: bool = False) -> None: self.zipf: ZipFile = zipf if follow_formatting: self.make_unique_filename: Callable[..., Path | str] = ( @@ -22,6 +22,7 @@ class BulkArchiveStrategy: def _filename_only( self, doc: Document, + *, archive: bool = False, folder: str = "", ) -> str: @@ -33,7 +34,10 @@ class BulkArchiveStrategy: """ counter = 0 while True: - filename: str = folder + doc.get_public_filename(archive, counter) + filename: str = folder + doc.get_public_filename( + archive=archive, + counter=counter, + ) if filename in self.zipf.namelist(): counter += 1 else: @@ -42,6 +46,7 @@ class BulkArchiveStrategy: def _formatted_filepath( self, doc: Document, + *, archive: bool = False, folder: str = "", ) -> Path: diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index 0aadcc295..f6adfc8a9 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -245,6 +245,7 @@ def reprocess(doc_ids: list[int]) -> Literal["OK"]: def set_permissions( doc_ids: list[int], set_permissions, + *, owner=None, merge=False, ) -> Literal["OK"]: @@ -309,6 +310,7 @@ def rotate(doc_ids: list[int], degrees: int) -> Literal["OK"]: def merge( doc_ids: list[int], + *, metadata_document_id: int | None = None, delete_originals: bool = False, user: User | None = None, @@ -387,6 +389,7 @@ def merge( def split( doc_ids: list[int], pages: list[list[int]], + *, delete_originals: bool = False, user: User | None = None, ) -> Literal["OK"]: diff --git a/src/documents/file_handling.py b/src/documents/file_handling.py index 4198ecabb..3d1a643df 100644 --- a/src/documents/file_handling.py +++ b/src/documents/file_handling.py @@ -43,7 +43,7 @@ def delete_empty_directories(directory, root): directory = os.path.normpath(os.path.dirname(directory)) -def generate_unique_filename(doc, archive_filename=False): +def generate_unique_filename(doc, *, archive_filename=False): """ Generates a unique filename for doc in settings.ORIGINALS_DIR. @@ -77,7 +77,7 @@ def generate_unique_filename(doc, archive_filename=False): while True: new_filename = generate_filename( doc, - counter, + counter=counter, archive_filename=archive_filename, ) if new_filename == old_filename: @@ -92,6 +92,7 @@ def generate_unique_filename(doc, archive_filename=False): def generate_filename( doc: Document, + *, counter=0, append_gpg=True, archive_filename=False, diff --git a/src/documents/filters.py b/src/documents/filters.py index 142f3f519..fab029312 100644 --- a/src/documents/filters.py +++ b/src/documents/filters.py @@ -97,7 +97,7 @@ class StoragePathFilterSet(FilterSet): class ObjectFilter(Filter): - def __init__(self, exclude=False, in_list=False, field_name=""): + def __init__(self, *, exclude=False, in_list=False, field_name=""): super().__init__() self.exclude = exclude self.in_list = in_list diff --git a/src/documents/index.py b/src/documents/index.py index 4c5afb505..4b11325ff 100644 --- a/src/documents/index.py +++ b/src/documents/index.py @@ -85,7 +85,7 @@ def get_schema() -> Schema: ) -def open_index(recreate=False) -> FileIndex: +def open_index(*, recreate=False) -> FileIndex: try: if exists_in(settings.INDEX_DIR) and not recreate: return open_dir(settings.INDEX_DIR, schema=get_schema()) @@ -101,7 +101,7 @@ def open_index(recreate=False) -> FileIndex: @contextmanager -def open_index_writer(optimize=False) -> AsyncWriter: +def open_index_writer(*, optimize=False) -> AsyncWriter: writer = AsyncWriter(open_index()) try: @@ -425,7 +425,7 @@ def autocomplete( def get_permissions_criterias(user: User | None = None) -> list: - user_criterias = [query.Term("has_owner", False)] + user_criterias = [query.Term("has_owner", text=False)] if user is not None: if user.is_superuser: # superusers see all docs user_criterias = [] diff --git a/src/documents/management/commands/convert_mariadb_uuid.py b/src/documents/management/commands/convert_mariadb_uuid.py index 4000e67cb..76ccf9e76 100644 --- a/src/documents/management/commands/convert_mariadb_uuid.py +++ b/src/documents/management/commands/convert_mariadb_uuid.py @@ -9,7 +9,7 @@ class Command(BaseCommand): # This code is taken almost entirely from https://github.com/wagtail/wagtail/pull/11912 with all credit to the original author. help = "Converts UUID columns from char type to the native UUID type used in MariaDB 10.7+ and Django 5.0+." - def convert_field(self, model, field_name, null=False): + def convert_field(self, model, field_name, *, null=False): if model._meta.get_field(field_name).model != model: # pragma: no cover # Field is inherited from a parent model return diff --git a/src/documents/management/commands/document_consumer.py b/src/documents/management/commands/document_consumer.py index 6b2706733..36dcc7706 100644 --- a/src/documents/management/commands/document_consumer.py +++ b/src/documents/management/commands/document_consumer.py @@ -248,15 +248,15 @@ class Command(BaseCommand): return if settings.CONSUMER_POLLING == 0 and INotify: - self.handle_inotify(directory, recursive, options["testing"]) + self.handle_inotify(directory, recursive, is_testing=options["testing"]) else: if INotify is None and settings.CONSUMER_POLLING == 0: # pragma: no cover logger.warning("Using polling as INotify import failed") - self.handle_polling(directory, recursive, options["testing"]) + self.handle_polling(directory, recursive, is_testing=options["testing"]) logger.debug("Consumer exiting.") - def handle_polling(self, directory, recursive, is_testing: bool): + def handle_polling(self, directory, recursive, *, is_testing: bool): logger.info(f"Polling directory for changes: {directory}") timeout = None @@ -283,7 +283,7 @@ class Command(BaseCommand): observer.stop() observer.join() - def handle_inotify(self, directory, recursive, is_testing: bool): + def handle_inotify(self, directory, recursive, *, is_testing: bool): logger.info(f"Using inotify to watch directory for changes: {directory}") timeout_ms = None diff --git a/src/documents/migrations/1012_fix_archive_files.py b/src/documents/migrations/1012_fix_archive_files.py index 1d12c439b..46951471e 100644 --- a/src/documents/migrations/1012_fix_archive_files.py +++ b/src/documents/migrations/1012_fix_archive_files.py @@ -84,7 +84,7 @@ def source_path(doc): return os.path.join(settings.ORIGINALS_DIR, fname) -def generate_unique_filename(doc, archive_filename=False): +def generate_unique_filename(doc, *, archive_filename=False): if archive_filename: old_filename = doc.archive_filename root = settings.ARCHIVE_DIR @@ -97,7 +97,7 @@ def generate_unique_filename(doc, archive_filename=False): while True: new_filename = generate_filename( doc, - counter, + counter=counter, archive_filename=archive_filename, ) if new_filename == old_filename: @@ -110,7 +110,7 @@ def generate_unique_filename(doc, archive_filename=False): return new_filename -def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False): +def generate_filename(doc, *, counter=0, append_gpg=True, archive_filename=False): path = "" try: diff --git a/src/documents/models.py b/src/documents/models.py index e7d866e24..25e3c62fd 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -337,7 +337,7 @@ class Document(SoftDeleteModel, ModelWithOwner): def archive_file(self): return open(self.archive_path, "rb") - def get_public_filename(self, archive=False, counter=0, suffix=None) -> str: + def get_public_filename(self, *, archive=False, counter=0, suffix=None) -> str: """ Returns a sanitized filename for the document, not including any paths. """ diff --git a/src/documents/parsers.py b/src/documents/parsers.py index d840817e4..28d903fdd 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -133,6 +133,7 @@ def get_parser_class_for_mime_type(mime_type: str) -> type["DocumentParser"] | N def run_convert( input_file, output_file, + *, density=None, scale=None, alpha=None, diff --git a/src/documents/permissions.py b/src/documents/permissions.py index 464916ad4..4380c6994 100644 --- a/src/documents/permissions.py +++ b/src/documents/permissions.py @@ -58,7 +58,7 @@ def get_groups_with_only_permission(obj, codename): return Group.objects.filter(id__in=group_object_perm_group_ids).distinct() -def set_permissions_for_object(permissions: list[str], object, merge: bool = False): +def set_permissions_for_object(permissions: list[str], object, *, merge: bool = False): """ Set permissions for an object. The permissions are given as a list of strings in the format "action_modelname", e.g. "view_document". diff --git a/src/documents/sanity_checker.py b/src/documents/sanity_checker.py index 9d44ff345..28d2024e7 100644 --- a/src/documents/sanity_checker.py +++ b/src/documents/sanity_checker.py @@ -57,7 +57,7 @@ class SanityCheckFailedException(Exception): pass -def check_sanity(progress=False) -> SanityCheckMessages: +def check_sanity(*, progress=False) -> SanityCheckMessages: messages = SanityCheckMessages() present_files = { diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index 4885910fd..1c4d36694 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -85,6 +85,7 @@ def _suggestion_printer( def set_correspondent( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, @@ -140,6 +141,7 @@ def set_correspondent( def set_document_type( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, @@ -196,6 +198,7 @@ def set_document_type( def set_tags( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, @@ -251,6 +254,7 @@ def set_tags( def set_storage_path( sender, document: Document, + *, logging_group=None, classifier: DocumentClassifier | None = None, replace=False, diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 8b0cbf249..d8539d1ab 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -63,7 +63,7 @@ def index_optimize(): writer.commit(optimize=True) -def index_reindex(progress_bar_disable=False): +def index_reindex(*, progress_bar_disable=False): documents = Document.objects.all() ix = index.open_index(recreate=True) diff --git a/src/documents/tests/test_api_filter_by_custom_fields.py b/src/documents/tests/test_api_filter_by_custom_fields.py index deb97bf29..70d43dfde 100644 --- a/src/documents/tests/test_api_filter_by_custom_fields.py +++ b/src/documents/tests/test_api_filter_by_custom_fields.py @@ -165,6 +165,7 @@ class TestCustomFieldsSearch(DirectoriesMixin, APITestCase): self, query: list, reference_predicate: Callable[[DocumentWrapper], bool], + *, match_nothing_ok=False, ): """ diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index 7fde5f8ee..4a7145d34 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -535,7 +535,12 @@ class TestPDFActions(DirectoriesMixin, TestCase): metadata_document_id = self.doc1.id user = User.objects.create(username="test_user") - result = bulk_edit.merge(doc_ids, None, False, user) + result = bulk_edit.merge( + doc_ids, + metadata_document_id=None, + delete_originals=False, + user=user, + ) expected_filename = ( f"{'_'.join([str(doc_id) for doc_id in doc_ids])[:100]}_merged.pdf" @@ -638,7 +643,7 @@ class TestPDFActions(DirectoriesMixin, TestCase): doc_ids = [self.doc2.id] pages = [[1, 2], [3]] user = User.objects.create(username="test_user") - result = bulk_edit.split(doc_ids, pages, False, user) + result = bulk_edit.split(doc_ids, pages, delete_originals=False, user=user) self.assertEqual(mock_consume_file.call_count, 2) consume_file_args, _ = mock_consume_file.call_args self.assertEqual(consume_file_args[1].title, "B (split 2)") diff --git a/src/documents/tests/test_consumer.py b/src/documents/tests/test_consumer.py index aa452e15b..a862d7fa0 100644 --- a/src/documents/tests/test_consumer.py +++ b/src/documents/tests/test_consumer.py @@ -233,7 +233,7 @@ class FaultyGenericExceptionParser(_BaseTestParser): raise Exception("Generic exception.") -def fake_magic_from_file(file, mime=False): +def fake_magic_from_file(file, *, mime=False): if mime: if file.name.startswith("invalid_pdf"): return "application/octet-stream" diff --git a/src/documents/tests/test_delayedquery.py b/src/documents/tests/test_delayedquery.py index 1895bd6c6..3ee4fb15d 100644 --- a/src/documents/tests/test_delayedquery.py +++ b/src/documents/tests/test_delayedquery.py @@ -10,7 +10,7 @@ class TestDelayedQuery(TestCase): super().setUp() # all tests run without permission criteria, so has_no_owner query will always # be appended. - self.has_no_owner = query.Or([query.Term("has_owner", False)]) + self.has_no_owner = query.Or([query.Term("has_owner", text=False)]) def _get_testset__id__in(self, param, field): return ( @@ -43,12 +43,12 @@ class TestDelayedQuery(TestCase): def test_get_permission_criteria(self): # tests contains tuples of user instances and the expected filter tests = ( - (None, [query.Term("has_owner", False)]), + (None, [query.Term("has_owner", text=False)]), (User(42, username="foo", is_superuser=True), []), ( User(42, username="foo", is_superuser=False), [ - query.Term("has_owner", False), + query.Term("has_owner", text=False), query.Term("owner_id", 42), query.Term("viewer_id", "42"), ], diff --git a/src/documents/tests/test_management_consumer.py b/src/documents/tests/test_management_consumer.py index 7e2707403..808216d3d 100644 --- a/src/documents/tests/test_management_consumer.py +++ b/src/documents/tests/test_management_consumer.py @@ -93,7 +93,7 @@ class ConsumerThreadMixin(DocumentConsumeDelayMixin): else: print("Consumed a perfectly valid file.") # noqa: T201 - def slow_write_file(self, target, incomplete=False): + def slow_write_file(self, target, *, incomplete=False): with open(self.sample_file, "rb") as f: pdf_bytes = f.read() diff --git a/src/documents/tests/test_management_exporter.py b/src/documents/tests/test_management_exporter.py index 0a79b6cd7..eec2fcd4b 100644 --- a/src/documents/tests/test_management_exporter.py +++ b/src/documents/tests/test_management_exporter.py @@ -188,7 +188,7 @@ class TestExportImport( return manifest - def test_exporter(self, use_filename_format=False): + def test_exporter(self, *, use_filename_format=False): shutil.rmtree(os.path.join(self.dirs.media_dir, "documents")) shutil.copytree( os.path.join(os.path.dirname(__file__), "samples", "documents"), diff --git a/src/documents/tests/test_matchables.py b/src/documents/tests/test_matchables.py index 9ca23e53d..180cf77ed 100644 --- a/src/documents/tests/test_matchables.py +++ b/src/documents/tests/test_matchables.py @@ -23,6 +23,7 @@ class _TestMatchingBase(TestCase): match_algorithm: str, should_match: Iterable[str], no_match: Iterable[str], + *, case_sensitive: bool = False, ): for klass in (Tag, Correspondent, DocumentType): diff --git a/src/documents/views.py b/src/documents/views.py index 24578179a..f23c1b953 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1608,7 +1608,7 @@ class BulkDownloadView(GenericAPIView): strategy_class = ArchiveOnlyStrategy with zipfile.ZipFile(temp.name, "w", compression) as zipf: - strategy = strategy_class(zipf, follow_filename_format) + strategy = strategy_class(zipf, follow_formatting=follow_filename_format) for document in documents: strategy.add_document(document) @@ -1872,7 +1872,7 @@ class SharedLinkView(View): ) -def serve_file(doc: Document, use_archive: bool, disposition: str): +def serve_file(*, doc: Document, use_archive: bool, disposition: str): if use_archive: file_handle = doc.archive_file filename = doc.get_public_filename(archive=True) diff --git a/src/paperless/views.py b/src/paperless/views.py index bcabd182f..6d297c49b 100644 --- a/src/paperless/views.py +++ b/src/paperless/views.py @@ -148,7 +148,7 @@ class UserViewSet(ModelViewSet): ).first() if authenticator is not None: delete_and_cleanup(request, authenticator) - return Response(True) + return Response(data=True) else: return HttpResponseNotFound("TOTP not found") @@ -262,7 +262,7 @@ class TOTPView(GenericAPIView): ).first() if authenticator is not None: delete_and_cleanup(request, authenticator) - return Response(True) + return Response(data=True) else: return HttpResponseNotFound("TOTP not found") diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py index e25c4f227..cf35ea6cb 100644 --- a/src/paperless_mail/mail.py +++ b/src/paperless_mail/mail.py @@ -121,7 +121,7 @@ class MarkReadMailAction(BaseMailAction): return {"seen": False} def post_consume(self, M: MailBox, message_uid: str, parameter: str): - M.flag(message_uid, [MailMessageFlags.SEEN], True) + M.flag(message_uid, [MailMessageFlags.SEEN], value=True) class MoveMailAction(BaseMailAction): @@ -142,7 +142,7 @@ class FlagMailAction(BaseMailAction): return {"flagged": False} def post_consume(self, M: MailBox, message_uid: str, parameter: str): - M.flag(message_uid, [MailMessageFlags.FLAGGED], True) + M.flag(message_uid, [MailMessageFlags.FLAGGED], value=True) class TagMailAction(BaseMailAction): @@ -150,7 +150,7 @@ class TagMailAction(BaseMailAction): A mail action that tags mails after processing. """ - def __init__(self, parameter: str, supports_gmail_labels: bool): + def __init__(self, parameter: str, *, supports_gmail_labels: bool): # The custom tag should look like "apple:" if "apple:" in parameter.lower(): _, self.color = parameter.split(":") @@ -188,19 +188,19 @@ class TagMailAction(BaseMailAction): M.flag( message_uid, set(itertools.chain(*APPLE_MAIL_TAG_COLORS.values())), - False, + value=False, ) # Set new $MailFlagBits - M.flag(message_uid, APPLE_MAIL_TAG_COLORS.get(self.color), True) + M.flag(message_uid, APPLE_MAIL_TAG_COLORS.get(self.color), value=True) # Set the general \Flagged # This defaults to the "red" flag in AppleMail and # "stars" in Thunderbird or GMail - M.flag(message_uid, [MailMessageFlags.FLAGGED], True) + M.flag(message_uid, [MailMessageFlags.FLAGGED], value=True) elif self.keyword: - M.flag(message_uid, [self.keyword], True) + M.flag(message_uid, [self.keyword], value=True) else: raise MailError("No keyword specified.") @@ -268,7 +268,7 @@ def apply_mail_action( mailbox_login(M, account) M.folder.set(rule.folder) - action = get_rule_action(rule, supports_gmail_labels) + action = get_rule_action(rule, supports_gmail_labels=supports_gmail_labels) try: action.post_consume(M, message_uid, rule.action_parameter) except errors.ImapToolsError: @@ -356,7 +356,7 @@ def queue_consumption_tasks( ).delay() -def get_rule_action(rule: MailRule, supports_gmail_labels: bool) -> BaseMailAction: +def get_rule_action(rule: MailRule, *, supports_gmail_labels: bool) -> BaseMailAction: """ Returns a BaseMailAction instance for the given rule. """ @@ -370,12 +370,15 @@ def get_rule_action(rule: MailRule, supports_gmail_labels: bool) -> BaseMailActi elif rule.action == MailRule.MailAction.MARK_READ: return MarkReadMailAction() elif rule.action == MailRule.MailAction.TAG: - return TagMailAction(rule.action_parameter, supports_gmail_labels) + return TagMailAction( + rule.action_parameter, + supports_gmail_labels=supports_gmail_labels, + ) else: raise NotImplementedError("Unknown action.") # pragma: no cover -def make_criterias(rule: MailRule, supports_gmail_labels: bool): +def make_criterias(rule: MailRule, *, supports_gmail_labels: bool): """ Returns criteria to be applied to MailBox.fetch for the given rule. """ @@ -393,7 +396,10 @@ def make_criterias(rule: MailRule, supports_gmail_labels: bool): if rule.filter_body: criterias["body"] = rule.filter_body - rule_query = get_rule_action(rule, supports_gmail_labels).get_criteria() + rule_query = get_rule_action( + rule, + supports_gmail_labels=supports_gmail_labels, + ).get_criteria() if isinstance(rule_query, dict): if len(rule_query) or len(criterias): return AND(**rule_query, **criterias) @@ -563,7 +569,7 @@ class MailAccountHandler(LoggingMixin): total_processed_files += self._handle_mail_rule( M, rule, - supports_gmail_labels, + supports_gmail_labels=supports_gmail_labels, ) except Exception as e: self.log.exception( @@ -588,6 +594,7 @@ class MailAccountHandler(LoggingMixin): self, M: MailBox, rule: MailRule, + *, supports_gmail_labels: bool, ): folders = [rule.folder] @@ -616,7 +623,7 @@ class MailAccountHandler(LoggingMixin): f"does not exist in account {rule.account}", ) from err - criterias = make_criterias(rule, supports_gmail_labels) + criterias = make_criterias(rule, supports_gmail_labels=supports_gmail_labels) self.log.debug( f"Rule {rule}: Searching folder with criteria {criterias}", diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py index 2311c3009..a73f9cf34 100644 --- a/src/paperless_mail/tests/test_mail.py +++ b/src/paperless_mail/tests/test_mail.py @@ -124,7 +124,7 @@ class BogusMailBox(AbstractContextManager): if username != self.USERNAME or access_token != self.ACCESS_TOKEN: raise MailboxLoginError("BAD", "OK") - def fetch(self, criteria, mark_seen, charset="", bulk=True): + def fetch(self, criteria, mark_seen, charset="", *, bulk=True): msg = self.messages criteria = str(criteria).strip("()").split(" ") @@ -190,7 +190,7 @@ class BogusMailBox(AbstractContextManager): raise Exception -def fake_magic_from_buffer(buffer, mime=False): +def fake_magic_from_buffer(buffer, *, mime=False): if mime: if "PDF" in str(buffer): return "application/pdf" @@ -206,6 +206,7 @@ class MessageBuilder: def create_message( self, + *, attachments: int | list[_AttachmentDef] = 1, body: str = "", subject: str = "the subject", @@ -783,12 +784,18 @@ class TestMail( ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_handle_mail_account_delete(self): @@ -853,7 +860,7 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 2, ) @@ -861,7 +868,7 @@ class TestMail( self.mailMocker.apply_mail_actions() self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 1, ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) @@ -934,7 +941,12 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNKEYWORD processed", False)), + len( + self.mailMocker.bogus_mailbox.fetch( + "UNKEYWORD processed", + mark_seen=False, + ), + ), 2, ) @@ -943,7 +955,12 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNKEYWORD processed", False)), + len( + self.mailMocker.bogus_mailbox.fetch( + "UNKEYWORD processed", + mark_seen=False, + ), + ), 0, ) @@ -967,12 +984,18 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) criteria = NOT(gmail_label="processed") - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch(criteria, False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch(criteria, mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch(criteria, False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch(criteria, mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_tag_mail_action_applemail_wrong_input(self): @@ -980,7 +1003,7 @@ class TestMail( MailError, TagMailAction, "apple:black", - False, + supports_gmail_labels=False, ) def test_handle_mail_account_tag_applemail(self): @@ -1002,7 +1025,7 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 2, ) @@ -1010,7 +1033,7 @@ class TestMail( self.mailMocker.apply_mail_actions() self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNFLAGGED", mark_seen=False)), 0, ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) @@ -1324,13 +1347,19 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.mailMocker._queue_consumption_tasks_mock.assert_not_called() - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() self.assertEqual(self.mailMocker._queue_consumption_tasks_mock.call_count, 2) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_auth_plain_fallback_fails_still(self): @@ -1390,13 +1419,19 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) self.assertEqual(self.mailMocker._queue_consumption_tasks_mock.call_count, 0) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() self.assertEqual(self.mailMocker._queue_consumption_tasks_mock.call_count, 2) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 0) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 0, + ) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) def test_disabled_rule(self): @@ -1425,12 +1460,15 @@ class TestMail( self.mailMocker.apply_mail_actions() self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) - self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), + 2, + ) self.mail_account_handler.handle_mail_account(account) self.mailMocker.apply_mail_actions() self.assertEqual( - len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", mark_seen=False)), 2, ) # still 2 diff --git a/src/paperless_tesseract/parsers.py b/src/paperless_tesseract/parsers.py index e7968a61e..a8be899f5 100644 --- a/src/paperless_tesseract/parsers.py +++ b/src/paperless_tesseract/parsers.py @@ -214,6 +214,7 @@ class RasterisedDocumentParser(DocumentParser): mime_type, output_file, sidecar_file, + *, safe_fallback=False, ): if TYPE_CHECKING: