diff --git a/.dockerignore b/.dockerignore index 3754bfc1..181b30a8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -54,6 +54,7 @@ db.sqlite3-journal # React .DS_* *.log +*.tsbuildinfo logs **/*.backup.* **/*.back.* diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index fe467311..714eaed4 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -9,35 +9,35 @@ defaults: on: push: - branches: [ "main" ] + branches: ["main"] paths: - rsconcept/frontend/** - .github/workflows/frontend.yml pull_request: - branches: [ "main" ] + branches: ["main"] jobs: build: - runs-on: ubuntu-22.04 strategy: matrix: - node-version: [18.x] + node-version: [22.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache-dependency-path: rsconcept/frontend/package-lock.json - cache: 'npm' - - name: Build - run: | - npm ci - npm run build --if-present - - name: Test - run: | - npm test + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache-dependency-path: rsconcept/frontend/package-lock.json + cache: "npm" + - name: Build + run: | + npm install -g typescript vite jest + npm ci + npm run build --if-present + - name: Test + run: | + npm test diff --git a/.gitignore b/.gitignore index fa0da0f3..291ef36e 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ db.sqlite3-journal # React .DS_* *.log +*.tsbuildinfo logs **/*.backup.* **/*.back.* diff --git a/.vscode/settings.json b/.vscode/settings.json index 07c8043b..90b939b8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,7 +11,9 @@ "--multi-line", "3", "--project", - "apps" + "apps", + "--project", + "shared" ], "autopep8.args": [ "--max-line-length", @@ -38,6 +40,10 @@ { "name": "django", "depth": 5 + }, + { + "name": "djangorestframework", + "depth": 2 } ], "colorize.include": [".tsx", ".jsx", ".ts", ".js"], diff --git a/README.md b/README.md index ea99477d..51d4dd10 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ This readme file is used mostly to document project dependencies - Backticks - Svg Preview - TODO Highlight v2 + - Prettier
@@ -114,8 +115,10 @@ This readme file is used mostly to document project dependencies
   - Pylance
   - Pylint
-  - Django
   - autopep8
+  - isort
+  - Django
+  - SQLite
   
diff --git a/TODO.txt b/TODO.txt index d5bb5628..8c9ec45d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -11,7 +11,7 @@ For more specific TODOs see comments in code [Functionality - PENDING] - Search functionality for manuals - User notifications on edit - consider spam prevention and change aggregation -- Static analyzer for RSForm +- Static analyzer for RSForm as a whole: check term duplication and empty conventions - Content based search in Library - User profile: Settings + settings persistency @@ -25,12 +25,18 @@ For more specific TODOs see comments in code - Internationalization - at least english version. Consider react.intl - Focus on codemirror editor when label is clicked (need React 19 ref for clean code solution) +- Sitemap for better SEO and crawler optimization + [Tech] -- add debounce to some search fields - duplicate syntax parsing and type info calculations to client. Consider moving backend to Nodejs or embedding c++ lib - +- add debounce to some search fields - DataTable: fixed percentage columns, especially for SubstituteTable. Rework column sizing mechanics +- move autopep8 and isort settings from vscode settings to pyproject.toml +- Test UI for #enable-force-dark Chrome setting + +- Testing: frontend react components, testplane / playwright? +- Documentation: frontend base components at least [Deployment] @@ -39,7 +45,6 @@ For more specific TODOs see comments in code [Security] -- password-reset leaks info of email being used - improve nginx config. Consider DDOS and other types of attacks on infrastructure - recaptcha for create user and rest password https://yandex.cloud/ru/docs/smartcaptcha @@ -50,9 +55,17 @@ Research and consider integration - django-allauth - consider supporting popular auth providers - drf-messages +- skeleton loading +https://react.dev/reference/react/Suspense + - backend error message unification https://drf-standardized-errors.readthedocs.io/en/latest/error_response.html +- semantic json diff +https://stackoverflow.com/questions/28838170/multilevel-json-diff-in-python + +- Documentation platform. Consider diplodoc + - radix-ui - shadcn-ui @@ -60,6 +73,4 @@ https://drf-standardized-errors.readthedocs.io/en/latest/error_response.html - use-debounce - react-query -- react-hook-form - -- node-based UI \ No newline at end of file +- react-hook-form \ No newline at end of file diff --git a/rsconcept/backend/Dockerfile b/rsconcept/backend/Dockerfile index bb2db3f4..4a5521d9 100644 --- a/rsconcept/backend/Dockerfile +++ b/rsconcept/backend/Dockerfile @@ -1,7 +1,7 @@ # ========================================== # ============ Multi-stage build =========== # ========================================== -FROM ubuntu:jammy as python-base +FROM ubuntu:jammy AS python-base ENV DEBIAN_FRONTEND=noninteractive @@ -25,7 +25,7 @@ RUN apt-get update -qq && \ rm -rf /var/lib/apt/lists/* # ========= Builder ============== -FROM python-base as builder +FROM python-base AS builder # Set env variables ENV PYTHONDONTWRITEBYTECODE 1 @@ -65,9 +65,10 @@ RUN pip install --no-cache /wheels/* && \ rm -rf /wheels # Copy application sources and setup permissions -COPY apps/ ./apps COPY project/ ./project +COPY shared/ ./shared COPY fixtures/ ./fixtures +COPY apps/ ./apps COPY manage.py entrypoint.sh ./ RUN sed -i 's/\r$//g' $APP_HOME/entrypoint.sh && \ chmod +x $APP_HOME/entrypoint.sh && \ diff --git a/rsconcept/backend/apps/oss/__init__.py b/rsconcept/backend/apps/oss/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rsconcept/backend/apps/oss/admin.py b/rsconcept/backend/apps/oss/admin.py new file mode 100644 index 00000000..3bf56a23 --- /dev/null +++ b/rsconcept/backend/apps/oss/admin.py @@ -0,0 +1,14 @@ +''' Admin view: OperationSchema. ''' +from django.contrib import admin + +from . import models + + +class OperationAdmin(admin.ModelAdmin): + ''' Admin model: Operation. ''' + ordering = ['oss'] + list_display = ['oss', 'operation_type', 'result', 'alias', 'title', 'comment', 'position_x', 'position_y'] + search_fields = ['operation_type', 'title', 'alias'] + + +admin.site.register(models.Operation, OperationAdmin) diff --git a/rsconcept/backend/apps/oss/apps.py b/rsconcept/backend/apps/oss/apps.py new file mode 100644 index 00000000..540eca26 --- /dev/null +++ b/rsconcept/backend/apps/oss/apps.py @@ -0,0 +1,8 @@ +''' Application: Operation Schema. ''' +from django.apps import AppConfig + + +class RsformConfig(AppConfig): + ''' Application config. ''' + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.oss' diff --git a/rsconcept/backend/apps/oss/migrations/0001_initial.py b/rsconcept/backend/apps/oss/migrations/0001_initial.py new file mode 100644 index 00000000..e5fc31f8 --- /dev/null +++ b/rsconcept/backend/apps/oss/migrations/0001_initial.py @@ -0,0 +1,69 @@ +# Generated by Django 5.0.7 on 2024-07-17 09:51 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('rsform', '0008_alter_libraryitem_item_type'), + ] + + operations = [ + migrations.CreateModel( + name='Operation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('operation_type', models.CharField(choices=[ + ('input', 'Input'), ('synthesis', 'Synthesis')], default='input', max_length=10, verbose_name='Тип')), + ('alias', models.CharField(blank=True, max_length=255, verbose_name='Шифр')), + ('title', models.TextField(blank=True, verbose_name='Название')), + ('comment', models.TextField(blank=True, verbose_name='Комментарий')), + ('position_x', models.FloatField(default=0, verbose_name='Положение по горизонтали')), + ('position_y', models.FloatField(default=0, verbose_name='Положение по вертикали')), + ('oss', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='items', to='rsform.libraryitem', verbose_name='Схема синтеза')), + ('result', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='producer', to='rsform.libraryitem', verbose_name='Связанная КС')), + ], + options={ + 'verbose_name': 'Операция', + 'verbose_name_plural': 'Операции', + }, + ), + migrations.CreateModel( + name='SynthesisSubstitution', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('transfer_term', models.BooleanField(default=False, verbose_name='Перенос термина')), + ('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + to='oss.operation', verbose_name='Операция')), + ('original', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='as_original', to='rsform.constituenta', verbose_name='Удаляемая конституента')), + ('substitution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='as_substitute', to='rsform.constituenta', verbose_name='Замещающая конституента')), + ], + options={ + 'verbose_name': 'Отождествление синтеза', + 'verbose_name_plural': 'Таблицы отождествлений', + }, + ), + migrations.CreateModel( + name='Argument', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('argument', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='descendants', to='oss.operation', verbose_name='Аргумент')), + ('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='arguments', to='oss.operation', verbose_name='Операция')), + ], + options={ + 'verbose_name': 'Аргумент', + 'verbose_name_plural': 'Аргументы операций', + 'unique_together': {('operation', 'argument')}, + }, + ), + ] diff --git a/rsconcept/backend/apps/oss/migrations/__init__.py b/rsconcept/backend/apps/oss/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rsconcept/backend/apps/oss/models/Argument.py b/rsconcept/backend/apps/oss/models/Argument.py new file mode 100644 index 00000000..3282113d --- /dev/null +++ b/rsconcept/backend/apps/oss/models/Argument.py @@ -0,0 +1,27 @@ +''' Models: Operation Argument in OSS. ''' +from django.db.models import CASCADE, ForeignKey, Model + + +class Argument(Model): + ''' Operation Argument.''' + operation: ForeignKey = ForeignKey( + verbose_name='Операция', + to='oss.Operation', + on_delete=CASCADE, + related_name='arguments' + ) + argument: ForeignKey = ForeignKey( + verbose_name='Аргумент', + to='oss.Operation', + on_delete=CASCADE, + related_name='descendants' + ) + + class Meta: + ''' Model metadata. ''' + verbose_name = 'Аргумент' + verbose_name_plural = 'Аргументы операций' + unique_together = [['operation', 'argument']] + + def __str__(self) -> str: + return f'{self.argument.pk} -> {self.operation.pk}' diff --git a/rsconcept/backend/apps/oss/models/Operation.py b/rsconcept/backend/apps/oss/models/Operation.py new file mode 100644 index 00000000..54b16740 --- /dev/null +++ b/rsconcept/backend/apps/oss/models/Operation.py @@ -0,0 +1,71 @@ +''' Models: Operation in OSS. ''' +from django.db.models import ( + CASCADE, + SET_NULL, + CharField, + FloatField, + ForeignKey, + Model, + TextChoices, + TextField +) + + +class OperationType(TextChoices): + ''' Type of operation. ''' + INPUT = 'input' + SYNTHESIS = 'synthesis' + + +class Operation(Model): + ''' Operational schema Unit.''' + oss: ForeignKey = ForeignKey( + verbose_name='Схема синтеза', + to='rsform.LibraryItem', + on_delete=CASCADE, + related_name='items' + ) + operation_type: CharField = CharField( + verbose_name='Тип', + max_length=10, + choices=OperationType.choices, + default=OperationType.INPUT + ) + result: ForeignKey = ForeignKey( + verbose_name='Связанная КС', + to='rsform.LibraryItem', + null=True, + on_delete=SET_NULL, + related_name='producer' + ) + + alias: CharField = CharField( + verbose_name='Шифр', + max_length=255, + blank=True + ) + title: TextField = TextField( + verbose_name='Название', + blank=True + ) + comment: TextField = TextField( + verbose_name='Комментарий', + blank=True + ) + + position_x: FloatField = FloatField( + verbose_name='Положение по горизонтали', + default=0 + ) + position_y: FloatField = FloatField( + verbose_name='Положение по вертикали', + default=0 + ) + + class Meta: + ''' Model metadata. ''' + verbose_name = 'Операция' + verbose_name_plural = 'Операции' + + def __str__(self) -> str: + return f'Операция {self.alias}' diff --git a/rsconcept/backend/apps/oss/models/SynthesisSubstitution.py b/rsconcept/backend/apps/oss/models/SynthesisSubstitution.py new file mode 100644 index 00000000..61c2da22 --- /dev/null +++ b/rsconcept/backend/apps/oss/models/SynthesisSubstitution.py @@ -0,0 +1,36 @@ +''' Models: SynthesisSubstitution. ''' +from django.db.models import CASCADE, BooleanField, ForeignKey, Model + + +class SynthesisSubstitution(Model): + ''' Substitutions as part of Synthesis operation in OSS.''' + operation: ForeignKey = ForeignKey( + verbose_name='Операция', + to='oss.Operation', + on_delete=CASCADE + ) + + original: ForeignKey = ForeignKey( + verbose_name='Удаляемая конституента', + to='rsform.Constituenta', + on_delete=CASCADE, + related_name='as_original' + ) + substitution: ForeignKey = ForeignKey( + verbose_name='Замещающая конституента', + to='rsform.Constituenta', + on_delete=CASCADE, + related_name='as_substitute' + ) + transfer_term: BooleanField = BooleanField( + verbose_name='Перенос термина', + default=False + ) + + class Meta: + ''' Model metadata. ''' + verbose_name = 'Отождествление синтеза' + verbose_name_plural = 'Таблицы отождествлений' + + def __str__(self) -> str: + return f'{self.original.pk} -> {self.substitution.pk}' diff --git a/rsconcept/backend/apps/oss/models/__init__.py b/rsconcept/backend/apps/oss/models/__init__.py new file mode 100644 index 00000000..baa166e3 --- /dev/null +++ b/rsconcept/backend/apps/oss/models/__init__.py @@ -0,0 +1,8 @@ +''' Django: Models. ''' + +from apps.rsform.models import LibraryItem, LibraryItemType + +from .api_OSS import OperationSchema +from .Argument import Argument +from .Operation import Operation, OperationType +from .SynthesisSubstitution import SynthesisSubstitution diff --git a/rsconcept/backend/apps/oss/models/api_OSS.py b/rsconcept/backend/apps/oss/models/api_OSS.py new file mode 100644 index 00000000..c4a3db7c --- /dev/null +++ b/rsconcept/backend/apps/oss/models/api_OSS.py @@ -0,0 +1,128 @@ +''' Models: OSS API. ''' +from typing import Optional + +from django.core.exceptions import ValidationError +from django.db import transaction +from django.db.models import QuerySet + +from apps.rsform.models import LibraryItem, LibraryItemType +from shared import messages as msg + +from .Argument import Argument +from .Operation import Operation +from .SynthesisSubstitution import SynthesisSubstitution + + +class OperationSchema: + ''' Operations schema API. ''' + + def __init__(self, item: LibraryItem): + if item.item_type != LibraryItemType.OPERATION_SCHEMA: + raise ValueError(msg.libraryTypeUnexpected()) + self.item = item + + @staticmethod + def create(**kwargs) -> 'OperationSchema': + item = LibraryItem.objects.create(item_type=LibraryItemType.OPERATION_SCHEMA, **kwargs) + return OperationSchema(item=item) + + def operations(self) -> QuerySet[Operation]: + ''' Get QuerySet containing all operations of current OSS. ''' + return Operation.objects.filter(oss=self.item) + + def arguments(self) -> QuerySet[Argument]: + ''' Operation arguments. ''' + return Argument.objects.filter(operation__oss=self.item) + + def substitutions(self) -> QuerySet[SynthesisSubstitution]: + ''' Operation substitutions. ''' + return SynthesisSubstitution.objects.filter(operation__oss=self.item) + + def update_positions(self, data: list[dict]): + ''' Update positions. ''' + lookup = {x['id']: x for x in data} + operations = self.operations() + for item in operations: + if item.pk in lookup: + item.position_x = lookup[item.pk]['position_x'] + item.position_y = lookup[item.pk]['position_y'] + Operation.objects.bulk_update(operations, ['position_x', 'position_y']) + + @transaction.atomic + def create_operation(self, **kwargs) -> Operation: + ''' Insert new operation. ''' + if kwargs['alias'] != '' and self.operations().filter(alias=kwargs['alias']).exists(): + raise ValidationError(msg.aliasTaken(kwargs['alias'])) + result = Operation.objects.create( + oss=self.item, + **kwargs + ) + self.item.save() + result.refresh_from_db() + return result + + @transaction.atomic + def delete_operation(self, operation: Operation): + ''' Delete operation. ''' + operation.delete() + + # deal with attached schema + # trigger on_change effects + + self.item.save() + + @transaction.atomic + def set_input(self, target: Operation, schema: Optional[LibraryItem]): + ''' Set input schema for operation. ''' + if schema == target.result: + return + if schema: + target.result = schema + target.alias = schema.alias + target.title = schema.title + target.comment = schema.comment + else: + target.result = None + target.save() + + # trigger on_change effects + + self.item.save() + + @transaction.atomic + def add_argument(self, operation: Operation, argument: Operation) -> Optional[Argument]: + ''' Add Argument to operation. ''' + if Argument.objects.filter(operation=operation, argument=argument).exists(): + return None + result = Argument.objects.create(operation=operation, argument=argument) + self.item.save() + return result + + @transaction.atomic + def clear_arguments(self, target: Operation): + ''' Clear all arguments for operation. ''' + if not Argument.objects.filter(operation=target).exists(): + return + + Argument.objects.filter(operation=target).delete() + SynthesisSubstitution.objects.filter(operation=target).delete() + + # trigger on_change effects + + self.item.save() + + @transaction.atomic + def set_substitutions(self, target: Operation, substitutes: list[dict]): + ''' Clear all arguments for operation. ''' + SynthesisSubstitution.objects.filter(operation=target).delete() + for sub in substitutes: + SynthesisSubstitution.objects.create( + operation=target, + original=sub['original'], + substitution=sub['substitution'], + transfer_term=sub['transfer_term'] + ) + + # trigger on_change effects + + self.item.save() diff --git a/rsconcept/backend/apps/oss/serializers/__init__.py b/rsconcept/backend/apps/oss/serializers/__init__.py new file mode 100644 index 00000000..fe010453 --- /dev/null +++ b/rsconcept/backend/apps/oss/serializers/__init__.py @@ -0,0 +1,13 @@ +''' REST API: Serializers. ''' + +from apps.rsform.serializers import LibraryItemSerializer + +from .basics import OperationPositionSerializer, PositionsSerializer +from .data_access import ( + ArgumentSerializer, + OperationCreateSerializer, + OperationDeleteSerializer, + OperationSchemaSerializer, + OperationSerializer +) +from .schema_typing import NewOperationResponse diff --git a/rsconcept/backend/apps/oss/serializers/basics.py b/rsconcept/backend/apps/oss/serializers/basics.py new file mode 100644 index 00000000..d597ca5a --- /dev/null +++ b/rsconcept/backend/apps/oss/serializers/basics.py @@ -0,0 +1,16 @@ +''' Basic serializers that do not interact with database. ''' +from rest_framework import serializers + + +class OperationPositionSerializer(serializers.Serializer): + ''' Operation position. ''' + id = serializers.IntegerField() + position_x = serializers.FloatField() + position_y = serializers.FloatField() + + +class PositionsSerializer(serializers.Serializer): + ''' Operations position for OperationSchema. ''' + positions = serializers.ListField( + child=OperationPositionSerializer() + ) diff --git a/rsconcept/backend/apps/oss/serializers/data_access.py b/rsconcept/backend/apps/oss/serializers/data_access.py new file mode 100644 index 00000000..eb161ff6 --- /dev/null +++ b/rsconcept/backend/apps/oss/serializers/data_access.py @@ -0,0 +1,108 @@ +''' Serializers for persistent data manipulation. ''' +from typing import cast + +from django.db.models import F +from rest_framework import serializers +from rest_framework.serializers import PrimaryKeyRelatedField as PKField + +from apps.rsform.models import LibraryItem +from apps.rsform.serializers import LibraryItemDetailsSerializer +from shared import messages as msg + +from ..models import Argument, Operation, OperationSchema, OperationType +from .basics import OperationPositionSerializer + + +class OperationSerializer(serializers.ModelSerializer): + ''' Serializer: Operation data. ''' + class Meta: + ''' serializer metadata. ''' + model = Operation + fields = '__all__' + read_only_fields = ('id', 'oss') + + +class ArgumentSerializer(serializers.ModelSerializer): + ''' Serializer: Operation data. ''' + class Meta: + ''' serializer metadata. ''' + model = Argument + fields = ('operation', 'argument') + + +class OperationCreateSerializer(serializers.Serializer): + ''' Serializer: Operation creation. ''' + class OperationData(serializers.ModelSerializer): + ''' Serializer: Operation creation data. ''' + alias = serializers.CharField() + operation_type = serializers.ChoiceField(OperationType.choices) + + class Meta: + ''' serializer metadata. ''' + model = Operation + fields = \ + 'alias', 'operation_type', 'title', \ + 'comment', 'position_x', 'position_y' + + item_data = OperationData() + positions = serializers.ListField( + child=OperationPositionSerializer(), + default=[] + ) + + +class OperationDeleteSerializer(serializers.Serializer): + ''' Serializer: Delete operation. ''' + target = PKField(many=False, queryset=Operation.objects.all()) + positions = serializers.ListField( + child=OperationPositionSerializer(), + default=[] + ) + + def validate(self, attrs): + oss = cast(LibraryItem, self.context['oss']) + operation = cast(Operation, attrs['target']) + if oss and operation.oss != oss: + raise serializers.ValidationError({ + f'{operation.id}': msg.operationNotOwned(oss.title) + }) + self.instance = operation + return attrs + + +class OperationSchemaSerializer(serializers.ModelSerializer): + ''' Serializer: Detailed data for OSS. ''' + items = serializers.ListField( + child=OperationSerializer() + ) + graph = serializers.ListField( + child=ArgumentSerializer() + ) + + class Meta: + ''' serializer metadata. ''' + model = LibraryItem + fields = '__all__' + + def to_representation(self, instance: LibraryItem): + result = LibraryItemDetailsSerializer(instance).data + oss = OperationSchema(instance) + result['items'] = [] + for operation in oss.operations(): + result['items'].append(OperationSerializer(operation).data) + result['graph'] = [] + for argument in oss.arguments(): + result['graph'].append(ArgumentSerializer(argument).data) + result['substitutions'] = [] + for substitution in oss.substitutions().values( + 'operation', + 'original', + 'transfer_term', + 'substitution', + original_alias=F('original__alias'), + original_term=F('original__term_resolved'), + substitution_alias=F('substitution__alias'), + substitution_term=F('substitution__term_resolved'), + ): + result['substitutions'].append(substitution) + return result diff --git a/rsconcept/backend/apps/oss/serializers/schema_typing.py b/rsconcept/backend/apps/oss/serializers/schema_typing.py new file mode 100644 index 00000000..c0cf5b31 --- /dev/null +++ b/rsconcept/backend/apps/oss/serializers/schema_typing.py @@ -0,0 +1,10 @@ +''' Utility serializers for REST API schema - SHOULD NOT BE ACCESSED DIRECTLY. ''' +from rest_framework import serializers + +from .data_access import OperationSchemaSerializer, OperationSerializer + + +class NewOperationResponse(serializers.Serializer): + ''' Serializer: Create operation response. ''' + new_operation = OperationSerializer() + oss = OperationSchemaSerializer() diff --git a/rsconcept/backend/apps/oss/tests/__init__.py b/rsconcept/backend/apps/oss/tests/__init__.py new file mode 100644 index 00000000..a27a6767 --- /dev/null +++ b/rsconcept/backend/apps/oss/tests/__init__.py @@ -0,0 +1,3 @@ +''' Tests. ''' +from .s_models import * +from .s_views import * diff --git a/rsconcept/backend/apps/oss/tests/s_models/__init__.py b/rsconcept/backend/apps/oss/tests/s_models/__init__.py new file mode 100644 index 00000000..4458b11f --- /dev/null +++ b/rsconcept/backend/apps/oss/tests/s_models/__init__.py @@ -0,0 +1 @@ +''' Tests for Django Models. ''' diff --git a/rsconcept/backend/apps/oss/tests/s_views/__init__.py b/rsconcept/backend/apps/oss/tests/s_views/__init__.py new file mode 100644 index 00000000..10c776a8 --- /dev/null +++ b/rsconcept/backend/apps/oss/tests/s_views/__init__.py @@ -0,0 +1,2 @@ +''' Tests for REST API. ''' +from .t_oss import * diff --git a/rsconcept/backend/apps/oss/tests/s_views/t_oss.py b/rsconcept/backend/apps/oss/tests/s_views/t_oss.py new file mode 100644 index 00000000..1247148a --- /dev/null +++ b/rsconcept/backend/apps/oss/tests/s_views/t_oss.py @@ -0,0 +1,189 @@ +''' Testing API: Operation Schema. ''' + +from rest_framework import status + +from apps.oss.models import Operation, OperationSchema, OperationType +from apps.rsform.models import AccessPolicy, LibraryItem, LibraryItemType, LocationHead, RSForm +from shared.EndpointTester import EndpointTester, decl_endpoint + + +class TestOssViewset(EndpointTester): + ''' Testing OSS view. ''' + + def setUp(self): + super().setUp() + self.owned = OperationSchema.create(title='Test', alias='T1', owner=self.user) + self.owned_id = self.owned.item.pk + self.unowned = OperationSchema.create(title='Test2', alias='T2') + self.unowned_id = self.unowned.item.pk + self.private = OperationSchema.create(title='Test2', alias='T2', access_policy=AccessPolicy.PRIVATE) + self.private_id = self.private.item.pk + self.invalid_id = self.private.item.pk + 1337 + + + def populateData(self): + self.ks1 = RSForm.create(alias='KS1', title='Test1') + self.ks1x1 = self.ks1.insert_new('X1', term_resolved='X1_1') + self.ks2 = RSForm.create(alias='KS2', title='Test2') + self.ks2x1 = self.ks2.insert_new('X2', term_resolved='X1_2') + self.operation1 = self.owned.create_operation( + alias='1', + operation_type=OperationType.INPUT, + result=self.ks1.item + ) + self.operation2 = self.owned.create_operation( + alias='2', + operation_type=OperationType.INPUT, + result=self.ks2.item + ) + self.operation3 = self.owned.create_operation( + alias='3', + operation_type=OperationType.SYNTHESIS + ) + self.owned.add_argument(self.operation3, self.operation1) + self.owned.add_argument(self.operation3, self.operation2) + self.owned.set_substitutions(self.operation3, [{ + 'original': self.ks1x1, + 'substitution': self.ks2x1, + 'transfer_term': False + }]) + + @decl_endpoint('/api/oss/{item}/details', method='get') + def test_details(self): + self.populateData() + + response = self.executeOK(item=self.owned_id) + self.assertEqual(response.data['owner'], self.owned.item.owner.pk) + self.assertEqual(response.data['title'], self.owned.item.title) + self.assertEqual(response.data['alias'], self.owned.item.alias) + self.assertEqual(response.data['location'], self.owned.item.location) + self.assertEqual(response.data['access_policy'], self.owned.item.access_policy) + self.assertEqual(response.data['visible'], self.owned.item.visible) + + self.assertEqual(response.data['item_type'], LibraryItemType.OPERATION_SCHEMA) + + self.assertEqual(len(response.data['items']), 3) + self.assertEqual(response.data['items'][0]['id'], self.operation1.pk) + self.assertEqual(response.data['items'][0]['operation_type'], self.operation1.operation_type) + + self.assertEqual(len(response.data['substitutions']), 1) + sub = response.data['substitutions'][0] + self.assertEqual(sub['operation'], self.operation3.pk) + self.assertEqual(sub['original'], self.ks1x1.pk) + self.assertEqual(sub['substitution'], self.ks2x1.pk) + self.assertEqual(sub['transfer_term'], False) + self.assertEqual(sub['original_alias'], self.ks1x1.alias) + self.assertEqual(sub['original_term'], self.ks1x1.term_resolved) + self.assertEqual(sub['substitution_alias'], self.ks2x1.alias) + self.assertEqual(sub['substitution_term'], self.ks2x1.term_resolved) + + graph = response.data['graph'] + self.assertEqual(len(graph), 2) + self.assertEqual(graph[0]['operation'], self.operation3.pk) + self.assertEqual(graph[0]['argument'], self.operation1.pk) + self.assertEqual(graph[1]['operation'], self.operation3.pk) + self.assertEqual(graph[1]['argument'], self.operation2.pk) + + self.executeOK(item=self.unowned_id) + self.executeForbidden(item=self.private_id) + + self.logout() + self.executeOK(item=self.owned_id) + self.executeOK(item=self.unowned_id) + self.executeForbidden(item=self.private_id) + + @decl_endpoint('/api/oss/{item}/update-positions', method='patch') + def test_update_positions(self): + self.populateData() + self.executeBadData(item=self.owned_id) + + data = {'positions': []} + self.executeOK(data=data) + + data = {'positions': [ + {'id': self.operation1.pk, 'position_x': 42.1, 'position_y': 1337}, + {'id': self.operation2.pk, 'position_x': 36.1, 'position_y': 1437}, + {'id': self.invalid_id, 'position_x': 31, 'position_y': 12}, + ]} + self.toggle_admin(True) + self.executeOK(data=data, item=self.unowned_id) + self.operation1.refresh_from_db() + self.assertNotEqual(self.operation1.position_x, data['positions'][0]['position_x']) + self.assertNotEqual(self.operation1.position_y, data['positions'][0]['position_y']) + + self.toggle_admin(False) + self.executeOK(data=data, item=self.owned_id) + self.operation1.refresh_from_db() + self.operation2.refresh_from_db() + self.assertEqual(self.operation1.position_x, data['positions'][0]['position_x']) + self.assertEqual(self.operation1.position_y, data['positions'][0]['position_y']) + self.assertEqual(self.operation2.position_x, data['positions'][1]['position_x']) + self.assertEqual(self.operation2.position_y, data['positions'][1]['position_y']) + + self.executeForbidden(data=data, item=self.unowned_id) + self.executeForbidden(item=self.private_id) + + + @decl_endpoint('/api/oss/{item}/create-operation', method='post') + def test_create_operation(self): + self.executeNotFound(item=self.invalid_id) + + self.populateData() + self.executeBadData(item=self.owned_id) + + data = { + 'item_data': { + 'alias': 'Test3', + 'title': 'Test title', + 'comment': 'Тест кириллицы', + 'position_x': 1, + 'position_y': 1, + }, + 'positions': [ + {'id': self.operation1.pk, 'position_x': 42.1, 'position_y': 1337} + ] + } + self.executeBadData(data=data) + + data['item_data']['operation_type'] = 'invalid' + self.executeBadData(data=data) + + data['item_data']['operation_type'] = OperationType.INPUT + response = self.executeCreated(data=data) + self.assertEqual(len(response.data['oss']['items']), 4) + new_operation = response.data['new_operation'] + self.assertEqual(new_operation['alias'], data['item_data']['alias']) + self.assertEqual(new_operation['operation_type'], data['item_data']['operation_type']) + self.assertEqual(new_operation['title'], data['item_data']['title']) + self.assertEqual(new_operation['comment'], data['item_data']['comment']) + self.assertEqual(new_operation['position_x'], data['item_data']['position_x']) + self.assertEqual(new_operation['position_y'], data['item_data']['position_y']) + self.operation1.refresh_from_db() + self.assertEqual(self.operation1.position_x, data['positions'][0]['position_x']) + self.assertEqual(self.operation1.position_y, data['positions'][0]['position_y']) + + self.executeForbidden(data=data, item=self.unowned_id) + self.toggle_admin(True) + self.executeCreated(data=data, item=self.unowned_id) + + @decl_endpoint('/api/oss/{item}/delete-operation', method='patch') + def test_delete_operation(self): + self.executeNotFound(item=self.invalid_id) + + self.populateData() + self.executeBadData(item=self.owned_id) + + data = { + 'positions': [] + } + self.executeBadData(data=data) + + data['target'] = self.operation1.pk + self.toggle_admin(True) + self.executeBadData(data=data, item=self.unowned_id) + self.logout() + self.executeForbidden(data=data, item=self.owned_id) + + self.login() + response = self.executeOK(data=data) + self.assertEqual(len(response.data['items']), 2) diff --git a/rsconcept/backend/apps/oss/urls.py b/rsconcept/backend/apps/oss/urls.py new file mode 100644 index 00000000..04f3a316 --- /dev/null +++ b/rsconcept/backend/apps/oss/urls.py @@ -0,0 +1,12 @@ +''' Routing: Operation Schema. ''' +from django.urls import include, path +from rest_framework import routers + +from . import views + +library_router = routers.SimpleRouter(trailing_slash=False) +library_router.register('oss', views.OssViewSet, 'OSS') + +urlpatterns = [ + path('', include(library_router.urls)), +] diff --git a/rsconcept/backend/apps/oss/views/__init__.py b/rsconcept/backend/apps/oss/views/__init__.py new file mode 100644 index 00000000..d9319816 --- /dev/null +++ b/rsconcept/backend/apps/oss/views/__init__.py @@ -0,0 +1,2 @@ +''' REST API: Endpoint processors. ''' +from .oss import OssViewSet diff --git a/rsconcept/backend/apps/oss/views/oss.py b/rsconcept/backend/apps/oss/views/oss.py new file mode 100644 index 00000000..b824f321 --- /dev/null +++ b/rsconcept/backend/apps/oss/views/oss.py @@ -0,0 +1,141 @@ +''' Endpoints for OSS. ''' +from typing import cast + +from django.db import transaction +from drf_spectacular.utils import extend_schema, extend_schema_view +from rest_framework import generics +from rest_framework import status as c +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.request import Request +from rest_framework.response import Response + +from shared import permissions + +from .. import models as m +from .. import serializers as s + + +@extend_schema(tags=['OSS']) +@extend_schema_view() +class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView): + ''' Endpoint: OperationSchema. ''' + queryset = m.LibraryItem.objects.filter(item_type=m.LibraryItemType.OPERATION_SCHEMA) + serializer_class = s.LibraryItemSerializer + + def _get_schema(self) -> m.OperationSchema: + return m.OperationSchema(cast(m.LibraryItem, self.get_object())) + + def get_permissions(self): + ''' Determine permission class. ''' + if self.action in [ + 'create_operation', + 'delete_operation', + 'update_positions' + ]: + permission_list = [permissions.ItemEditor] + elif self.action in ['details']: + permission_list = [permissions.ItemAnyone] + else: + permission_list = [permissions.Anyone] + return [permission() for permission in permission_list] + + @extend_schema( + summary='get operations data', + tags=['OSS'], + request=None, + responses={ + c.HTTP_200_OK: s.OperationSchemaSerializer, + c.HTTP_404_NOT_FOUND: None + } + ) + @action(detail=True, methods=['get'], url_path='details') + def details(self, request: Request, pk): + ''' Endpoint: Detailed OSS data. ''' + serializer = s.OperationSchemaSerializer(cast(m.LibraryItem, self.get_object())) + return Response( + status=c.HTTP_200_OK, + data=serializer.data + ) + + @extend_schema( + summary='update positions', + tags=['OSS'], + request=s.PositionsSerializer, + responses={ + c.HTTP_200_OK: None, + c.HTTP_403_FORBIDDEN: None, + c.HTTP_404_NOT_FOUND: None + } + ) + @action(detail=True, methods=['patch'], url_path='update-positions') + def update_positions(self, request: Request, pk): + ''' Endpoint: Update operations positions. ''' + schema = self._get_schema() + serializer = s.PositionsSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + schema.update_positions(serializer.validated_data['positions']) + return Response(status=c.HTTP_200_OK) + + @extend_schema( + summary='create operation', + tags=['OSS'], + request=s.OperationCreateSerializer(), + responses={ + c.HTTP_201_CREATED: s.NewOperationResponse, + c.HTTP_400_BAD_REQUEST: None, + c.HTTP_403_FORBIDDEN: None, + c.HTTP_404_NOT_FOUND: None + } + ) + @action(detail=True, methods=['post'], url_path='create-operation') + def create_operation(self, request: Request, pk): + ''' Create new operation. ''' + schema = self._get_schema() + serializer = s.OperationCreateSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + with transaction.atomic(): + schema.update_positions(serializer.validated_data['positions']) + new_operation = schema.create_operation(**serializer.validated_data['item_data']) + schema.item.refresh_from_db() + + response = Response( + status=c.HTTP_201_CREATED, + data={ + 'new_operation': s.OperationSerializer(new_operation).data, + 'oss': s.OperationSchemaSerializer(schema.item).data + } + ) + return response + + @extend_schema( + summary='delete operation', + tags=['OSS'], + request=s.OperationDeleteSerializer, + responses={ + c.HTTP_200_OK: s.OperationSchemaSerializer, + c.HTTP_400_BAD_REQUEST: None, + c.HTTP_403_FORBIDDEN: None, + c.HTTP_404_NOT_FOUND: None + } + ) + @action(detail=True, methods=['patch'], url_path='delete-operation') + def delete_operation(self, request: Request, pk): + ''' Endpoint: Delete operation. ''' + schema = self._get_schema() + serializer = s.OperationDeleteSerializer( + data=request.data, + context={'oss': schema.item} + ) + serializer.is_valid(raise_exception=True) + + with transaction.atomic(): + schema.update_positions(serializer.validated_data['positions']) + schema.delete_operation(serializer.validated_data['target']) + schema.item.refresh_from_db() + + return Response( + status=c.HTTP_200_OK, + data=s.OperationSchemaSerializer(schema.item).data + ) diff --git a/rsconcept/backend/apps/rsform/graph.py b/rsconcept/backend/apps/rsform/graph.py index 4177840d..b6a749ad 100644 --- a/rsconcept/backend/apps/rsform/graph.py +++ b/rsconcept/backend/apps/rsform/graph.py @@ -91,7 +91,7 @@ class Graph(Generic[ItemType]): if len(self.inputs[node_id]) == 0: continue for parent in self.inputs[node_id]: - result[parent] = result[parent] + [id for id in result[node_id] if not id in result[parent]] + result[parent] = result[parent] + [id for id in result[node_id] if id not in result[parent]] return result def topological_order(self) -> list[ItemType]: diff --git a/rsconcept/backend/apps/rsform/migrations/0008_alter_libraryitem_item_type.py b/rsconcept/backend/apps/rsform/migrations/0008_alter_libraryitem_item_type.py new file mode 100644 index 00000000..ef7f844d --- /dev/null +++ b/rsconcept/backend/apps/rsform/migrations/0008_alter_libraryitem_item_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.7 on 2024-07-17 09:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('rsform', '0007_location_and_flags'), + ] + + operations = [ + migrations.AlterField( + model_name='libraryitem', + name='item_type', + field=models.CharField(choices=[('rsform', 'Rsform'), ('oss', 'Operation Schema')], max_length=50, verbose_name='Тип'), + ), + ] diff --git a/rsconcept/backend/apps/rsform/models/Editor.py b/rsconcept/backend/apps/rsform/models/Editor.py index 2f9ce6a0..d43ae17c 100644 --- a/rsconcept/backend/apps/rsform/models/Editor.py +++ b/rsconcept/backend/apps/rsform/models/Editor.py @@ -60,12 +60,12 @@ class Editor(Model): ''' Set editors for item. ''' processed: list[User] = [] for editor_item in Editor.objects.filter(item=item): - if not editor_item.editor in users: + if editor_item.editor not in users: editor_item.delete() else: processed.append(editor_item.editor) for user in users: - if not user in processed: + if user not in processed: processed.append(user) Editor.objects.create(item=item, editor=user) diff --git a/rsconcept/backend/apps/rsform/models/LibraryItem.py b/rsconcept/backend/apps/rsform/models/LibraryItem.py index 56813796..d4daf365 100644 --- a/rsconcept/backend/apps/rsform/models/LibraryItem.py +++ b/rsconcept/backend/apps/rsform/models/LibraryItem.py @@ -9,6 +9,7 @@ from django.db.models import ( DateTimeField, ForeignKey, Model, + QuerySet, TextChoices, TextField ) @@ -23,7 +24,7 @@ from .Version import Version class LibraryItemType(TextChoices): ''' Type of library items ''' RSFORM = 'rsform' - OPERATIONS_SCHEMA = 'oss' + OPERATION_SCHEMA = 'oss' class AccessPolicy(TextChoices): @@ -113,17 +114,17 @@ class LibraryItem(Model): def get_absolute_url(self): return f'/api/library/{self.pk}' - def subscribers(self) -> list[Subscription]: + def subscribers(self) -> list[User]: ''' Get all subscribers for this item. ''' - return [subscription.user for subscription in Subscription.objects.filter(item=self.pk)] + return [subscription.user for subscription in Subscription.objects.filter(item=self.pk).only('user')] - def versions(self) -> list[Version]: - ''' Get all Versions of this item. ''' - return list(Version.objects.filter(item=self.pk).order_by('-time_create')) - - def editors(self) -> list[Editor]: + def editors(self) -> list[User]: ''' Get all Editors of this item. ''' - return [item.editor for item in Editor.objects.filter(item=self.pk)] + return [item.editor for item in Editor.objects.filter(item=self.pk).only('editor')] + + def versions(self) -> QuerySet[Version]: + ''' Get all Versions of this item. ''' + return Version.objects.filter(item=self.pk).order_by('-time_create') @transaction.atomic def save(self, *args, **kwargs): diff --git a/rsconcept/backend/apps/rsform/models/api_RSForm.py b/rsconcept/backend/apps/rsform/models/api_RSForm.py index e0c03e12..9790f6b0 100644 --- a/rsconcept/backend/apps/rsform/models/api_RSForm.py +++ b/rsconcept/backend/apps/rsform/models/api_RSForm.py @@ -1,13 +1,14 @@ ''' Models: RSForm API. ''' from copy import deepcopy -from typing import Optional, Union, cast +from typing import Optional, cast from cctext import Entity, Resolver, TermForm, extract_entities, split_grams from django.core.exceptions import ValidationError from django.db import transaction from django.db.models import QuerySet -from .. import messages as msg +from shared import messages as msg + from ..graph import Graph from .api_RSLanguage import ( extract_globals, @@ -39,7 +40,7 @@ class RSForm: def create(**kwargs) -> 'RSForm': return RSForm(LibraryItem.objects.create(item_type=LibraryItemType.RSFORM, **kwargs)) - def constituents(self) -> QuerySet['Constituenta']: + def constituents(self) -> QuerySet[Constituenta]: ''' Get QuerySet containing all constituents of current RSForm. ''' return Constituenta.objects.filter(schema=self.item) @@ -104,11 +105,39 @@ class RSForm: result = max(result, int(alias[1:])) return result + @transaction.atomic + def create_cst(self, data: dict, insert_after: Optional[Constituenta] = None) -> Constituenta: + ''' Create new cst from data. ''' + if insert_after is None: + position = _INSERT_LAST + else: + position = insert_after.order + 1 + result = self.insert_new(data['alias'], data['cst_type'], position) + result.convention = data.get('convention', '') + result.definition_formal = data.get('definition_formal', '') + result.term_forms = data.get('term_forms', []) + result.term_raw = data.get('term_raw', '') + result.definition_raw = data.get('definition_raw', '') + + if result.term_raw != '' or result.definition_raw != '': + resolver = self.resolver() + if result.term_raw != '': + resolved = resolver.resolve(result.term_raw) + result.term_resolved = resolved + resolver.context[result.alias] = Entity(result.alias, resolved) + if result.definition_raw != '': + result.definition_resolved = resolver.resolve(result.definition_raw) + + result.save() + self.on_term_change([result.id]) + result.refresh_from_db() + return result + @transaction.atomic def insert_new( self, alias: str, - cst_type: Union[CstType, None] = None, + cst_type: Optional[CstType] = None, position: int = _INSERT_LAST, **kwargs ) -> Constituenta: @@ -195,27 +224,6 @@ class RSForm: self.resolve_all_text() self.item.save() - @transaction.atomic - def create_cst(self, data: dict, insert_after: Optional[str] = None) -> Constituenta: - ''' Create new cst from data. ''' - resolver = self.resolver() - cst = self._insert_new(data, insert_after) - cst.convention = data.get('convention', '') - cst.definition_formal = data.get('definition_formal', '') - cst.term_forms = data.get('term_forms', []) - cst.term_raw = data.get('term_raw', '') - if cst.term_raw != '': - resolved = resolver.resolve(cst.term_raw) - cst.term_resolved = resolved - resolver.context[cst.alias] = Entity(cst.alias, resolved) - cst.definition_raw = data.get('definition_raw', '') - if cst.definition_raw != '': - cst.definition_resolved = resolver.resolve(cst.definition_raw) - cst.save() - self.on_term_change([cst.id]) - cst.refresh_from_db() - return cst - @transaction.atomic def substitute( self, @@ -363,13 +371,6 @@ class RSForm: cst.save() order += 1 - def _insert_new(self, data: dict, insert_after: Optional[str] = None) -> Constituenta: - if insert_after is not None: - cst_after = Constituenta.objects.get(pk=insert_after) - return self.insert_new(data['alias'], data['cst_type'], cst_after.order + 1) - else: - return self.insert_new(data['alias'], data['cst_type']) - def _graph_formal(self) -> Graph[int]: ''' Graph based on formal definitions. ''' result: Graph[int] = Graph() diff --git a/rsconcept/backend/apps/rsform/models/api_RSLanguage.py b/rsconcept/backend/apps/rsform/models/api_RSLanguage.py index d32a8c4e..26daa1bb 100644 --- a/rsconcept/backend/apps/rsform/models/api_RSLanguage.py +++ b/rsconcept/backend/apps/rsform/models/api_RSLanguage.py @@ -6,7 +6,8 @@ from typing import Set, Tuple, cast import pyconcept -from .. import messages as msg +from shared import messages as msg + from .Constituenta import CstType _RE_GLOBALS = r'[XCSADFPT]\d+' # cspell:disable-line diff --git a/rsconcept/backend/apps/rsform/serializers/__init__.py b/rsconcept/backend/apps/rsform/serializers/__init__.py index 5c0aaca3..797fcaef 100644 --- a/rsconcept/backend/apps/rsform/serializers/__init__.py +++ b/rsconcept/backend/apps/rsform/serializers/__init__.py @@ -22,6 +22,7 @@ from .data_access import ( InlineSynthesisSerializer, LibraryItemBaseSerializer, LibraryItemCloneSerializer, + LibraryItemDetailsSerializer, LibraryItemSerializer, RSFormParseSerializer, RSFormSerializer, diff --git a/rsconcept/backend/apps/rsform/serializers/basics.py b/rsconcept/backend/apps/rsform/serializers/basics.py index cc21c83f..a7a19c3b 100644 --- a/rsconcept/backend/apps/rsform/serializers/basics.py +++ b/rsconcept/backend/apps/rsform/serializers/basics.py @@ -4,7 +4,8 @@ from typing import cast from cctext import EntityReference, Reference, ReferenceType, Resolver, SyntacticReference from rest_framework import serializers -from .. import messages as msg +from shared import messages as msg + from ..models import AccessPolicy, validate_location @@ -34,7 +35,7 @@ class LocationSerializer(serializers.Serializer): class AccessPolicySerializer(serializers.Serializer): ''' Serializer: Constituenta renaming. ''' - access_policy = serializers.CharField(max_length=500) + access_policy = serializers.CharField() def validate(self, attrs): attrs = super().validate(attrs) diff --git a/rsconcept/backend/apps/rsform/serializers/data_access.py b/rsconcept/backend/apps/rsform/serializers/data_access.py index 2861eff7..b181fa74 100644 --- a/rsconcept/backend/apps/rsform/serializers/data_access.py +++ b/rsconcept/backend/apps/rsform/serializers/data_access.py @@ -7,7 +7,8 @@ from django.db import transaction from rest_framework import serializers from rest_framework.serializers import PrimaryKeyRelatedField as PKField -from .. import messages as msg +from shared import messages as msg + from ..models import Constituenta, CstType, LibraryItem, RSForm, Version from .basics import CstParseSerializer from .io_pyconcept import PyConceptAdapter @@ -140,6 +141,8 @@ class CstDetailsSerializer(serializers.ModelSerializer): class CstCreateSerializer(serializers.ModelSerializer): ''' Serializer: Constituenta creation. ''' insert_after = serializers.IntegerField(required=False, allow_null=True) + alias = serializers.CharField(max_length=8) + cst_type = serializers.ChoiceField(CstType.choices) class Meta: ''' serializer metadata. ''' diff --git a/rsconcept/backend/apps/rsform/serializers/io_files.py b/rsconcept/backend/apps/rsform/serializers/io_files.py index 00065236..afc59108 100644 --- a/rsconcept/backend/apps/rsform/serializers/io_files.py +++ b/rsconcept/backend/apps/rsform/serializers/io_files.py @@ -2,7 +2,8 @@ from django.db import transaction from rest_framework import serializers -from .. import messages as msg +from shared import messages as msg + from ..models import Constituenta, LibraryItem, RSForm from ..utils import fix_old_references diff --git a/rsconcept/backend/apps/rsform/serializers/io_pyconcept.py b/rsconcept/backend/apps/rsform/serializers/io_pyconcept.py index 1aa79820..0b767b62 100644 --- a/rsconcept/backend/apps/rsform/serializers/io_pyconcept.py +++ b/rsconcept/backend/apps/rsform/serializers/io_pyconcept.py @@ -4,7 +4,8 @@ from typing import Optional, Union, cast import pyconcept -from .. import messages as msg +from shared import messages as msg + from ..models import RSForm diff --git a/rsconcept/backend/apps/rsform/tests/__init__.py b/rsconcept/backend/apps/rsform/tests/__init__.py index 66f814e6..039a907c 100644 --- a/rsconcept/backend/apps/rsform/tests/__init__.py +++ b/rsconcept/backend/apps/rsform/tests/__init__.py @@ -1,5 +1,5 @@ ''' Tests. ''' -from .s_models.t_RSForm import * +from .s_models import * from .s_views import * from .t_graph import * from .t_imports import * diff --git a/rsconcept/backend/apps/rsform/tests/s_models/__init__.py b/rsconcept/backend/apps/rsform/tests/s_models/__init__.py index f2ec99ed..44295f0f 100644 --- a/rsconcept/backend/apps/rsform/tests/s_models/__init__.py +++ b/rsconcept/backend/apps/rsform/tests/s_models/__init__.py @@ -1,4 +1,4 @@ -''' Tests for REST API. ''' +''' Tests for Django Models. ''' from .t_Constituenta import * from .t_Editor import * from .t_LibraryItem import * diff --git a/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py b/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py index 7881f2b9..195bb080 100644 --- a/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py +++ b/rsconcept/backend/apps/rsform/tests/s_models/t_RSForm.py @@ -101,6 +101,26 @@ class TestRSForm(TestCase): self.assertEqual(x2.schema, self.schema.item) self.assertEqual(x1.order, 1) + def test_create_cst(self): + data = { + 'alias': 'X3', + 'cst_type': CstType.BASE, + 'term_raw': 'слон', + 'definition_raw': 'test', + 'convention': 'convention' + } + + x1 = self.schema.insert_new('X1') + x2 = self.schema.insert_new('X2') + x3 = self.schema.create_cst(data=data, insert_after=x1) + x2.refresh_from_db() + + self.assertEqual(x3.alias, data['alias']) + self.assertEqual(x3.term_raw, data['term_raw']) + self.assertEqual(x3.definition_raw, data['definition_raw']) + self.assertEqual(x2.order, 3) + self.assertEqual(x3.order, 2) + def test_create_cst_resolve(self): x1 = self.schema.insert_new( diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_cctext.py b/rsconcept/backend/apps/rsform/tests/s_views/t_cctext.py index 33c91811..01e2adc3 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_cctext.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_cctext.py @@ -1,7 +1,7 @@ ''' Testing views ''' from cctext import split_grams -from ..EndpointTester import EndpointTester, decl_endpoint +from shared.EndpointTester import EndpointTester, decl_endpoint class TestNaturalLanguageViews(EndpointTester): @@ -14,20 +14,20 @@ class TestNaturalLanguageViews(EndpointTester): @decl_endpoint(endpoint='/api/cctext/parse', method='post') def test_parse_text(self): data = {'text': 'синим слонам'} - response = self.executeOK(data) + response = self.executeOK(data=data) self._assert_tags(response.data['result'], 'datv,NOUN,plur,anim,masc') @decl_endpoint(endpoint='/api/cctext/inflect', method='post') def test_inflect(self): data = {'text': 'синий слон', 'grams': 'plur,datv'} - response = self.executeOK(data) + response = self.executeOK(data=data) self.assertEqual(response.data['result'], 'синим слонам') @decl_endpoint(endpoint='/api/cctext/generate-lexeme', method='post') def test_generate_lexeme(self): data = {'text': 'синий слон'} - response = self.executeOK(data) + response = self.executeOK(data=data) self.assertEqual(len(response.data['items']), 12) self.assertEqual(response.data['items'][0]['text'], 'синий слон') diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_constituents.py b/rsconcept/backend/apps/rsform/tests/s_views/t_constituents.py index a19cfc49..b11d950e 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_constituents.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_constituents.py @@ -1,7 +1,6 @@ ''' Testing API: Constituents. ''' from apps.rsform.models import Constituenta, CstType, RSForm - -from ..EndpointTester import EndpointTester, decl_endpoint +from shared.EndpointTester import EndpointTester, decl_endpoint class TestConstituentaAPI(EndpointTester): @@ -52,18 +51,18 @@ class TestConstituentaAPI(EndpointTester): @decl_endpoint('/api/constituents/{item}', method='patch') def test_partial_update(self): data = {'convention': 'tt'} - self.executeForbidden(data, item=self.cst2.pk) + self.executeForbidden(data=data, item=self.cst2.pk) self.logout() - self.executeForbidden(data, item=self.cst1.pk) + self.executeForbidden(data=data, item=self.cst1.pk) self.login() - response = self.executeOK(data, item=self.cst1.pk) + response = self.executeOK(data=data, item=self.cst1.pk) self.cst1.refresh_from_db() self.assertEqual(response.data['convention'], 'tt') self.assertEqual(self.cst1.convention, 'tt') - self.executeOK(data, item=self.cst1.pk) + self.executeOK(data=data, item=self.cst1.pk) @decl_endpoint('/api/constituents/{item}', method='patch') @@ -72,7 +71,7 @@ class TestConstituentaAPI(EndpointTester): 'term_raw': 'New term', 'definition_raw': 'New def' } - response = self.executeOK(data, item=self.cst3.pk) + response = self.executeOK(data=data, item=self.cst3.pk) self.cst3.refresh_from_db() self.assertEqual(response.data['term_resolved'], 'New term') self.assertEqual(self.cst3.term_resolved, 'New term') @@ -86,7 +85,7 @@ class TestConstituentaAPI(EndpointTester): 'term_raw': '@{X1|nomn,sing}', 'definition_raw': '@{X1|nomn,sing} @{X1|sing,datv}' } - response = self.executeOK(data, item=self.cst3.pk) + response = self.executeOK(data=data, item=self.cst3.pk) self.cst3.refresh_from_db() self.assertEqual(self.cst3.term_resolved, self.cst1.term_resolved) self.assertEqual(response.data['term_resolved'], self.cst1.term_resolved) @@ -97,7 +96,7 @@ class TestConstituentaAPI(EndpointTester): @decl_endpoint('/api/constituents/{item}', method='patch') def test_readonly_cst_fields(self): data = {'alias': 'X33', 'order': 10} - response = self.executeOK(data, item=self.cst1.pk) + response = self.executeOK(data=data, item=self.cst1.pk) self.assertEqual(response.data['alias'], 'X1') self.assertEqual(response.data['alias'], self.cst1.alias) self.assertEqual(response.data['order'], self.cst1.order) diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_library.py b/rsconcept/backend/apps/rsform/tests/s_views/t_library.py index 3c53d80e..d83700fc 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_library.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_library.py @@ -11,9 +11,8 @@ from apps.rsform.models import ( RSForm, Subscription ) - -from ..EndpointTester import EndpointTester, decl_endpoint -from ..testing_utils import response_contains +from shared.EndpointTester import EndpointTester, decl_endpoint +from shared.testing_utils import response_contains class TestLibraryViewset(EndpointTester): @@ -49,17 +48,17 @@ class TestLibraryViewset(EndpointTester): 'title': 'Title', 'alias': 'alias', } - self.executeBadData(data) + self.executeBadData(data=data) data = { - 'item_type': LibraryItemType.OPERATIONS_SCHEMA, + 'item_type': LibraryItemType.OPERATION_SCHEMA, 'title': 'Title', 'alias': 'alias', 'access_policy': AccessPolicy.PROTECTED, 'visible': False, 'read_only': True } - response = self.executeCreated(data) + response = self.executeCreated(data=data) self.assertEqual(response.data['owner'], self.user.pk) self.assertEqual(response.data['item_type'], data['item_type']) self.assertEqual(response.data['title'], data['title']) @@ -70,25 +69,25 @@ class TestLibraryViewset(EndpointTester): self.logout() data = {'title': 'Title2'} - self.executeForbidden(data) + self.executeForbidden(data=data) @decl_endpoint('/api/library/{item}', method='patch') def test_update(self): data = {'id': self.unowned.pk, 'title': 'New Title'} - self.executeNotFound(data, item=self.invalid_item) - self.executeForbidden(data, item=self.unowned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeForbidden(data=data, item=self.unowned.pk) self.toggle_editor(self.unowned, True) - response = self.executeOK(data, item=self.unowned.pk) + response = self.executeOK(data=data, item=self.unowned.pk) self.assertEqual(response.data['title'], data['title']) self.unowned.access_policy = AccessPolicy.PRIVATE self.unowned.save() - self.executeForbidden(data, item=self.unowned.pk) + self.executeForbidden(data=data, item=self.unowned.pk) data = {'id': self.owned.pk, 'title': 'New Title'} - response = self.executeOK(data, item=self.owned.pk) + response = self.executeOK(data=data, item=self.owned.pk) self.assertEqual(response.data['title'], data['title']) self.assertEqual(response.data['alias'], self.owned.alias) @@ -99,7 +98,7 @@ class TestLibraryViewset(EndpointTester): 'access_policy': AccessPolicy.PROTECTED, 'location': LocationHead.LIBRARY } - response = self.executeOK(data, item=self.owned.pk) + response = self.executeOK(data=data, item=self.owned.pk) self.assertEqual(response.data['title'], data['title']) self.assertEqual(response.data['owner'], self.owned.owner.pk) self.assertEqual(response.data['access_policy'], self.owned.access_policy) @@ -112,22 +111,22 @@ class TestLibraryViewset(EndpointTester): time_update = self.owned.time_update data = {'user': self.user.pk} - self.executeNotFound(data, item=self.invalid_item) - self.executeForbidden(data, item=self.unowned.pk) - self.executeOK(data, item=self.owned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeForbidden(data=data, item=self.unowned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.owner, self.user) data = {'user': self.user2.pk} - self.executeOK(data, item=self.owned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.owner, self.user2) self.assertEqual(self.owned.time_update, time_update) - self.executeForbidden(data, item=self.owned.pk) + self.executeForbidden(data=data, item=self.owned.pk) self.toggle_admin(True) data = {'user': self.user.pk} - self.executeOK(data, item=self.owned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.owner, self.user) @@ -136,20 +135,20 @@ class TestLibraryViewset(EndpointTester): time_update = self.owned.time_update data = {'access_policy': 'invalid'} - self.executeBadData(data, item=self.owned.pk) + self.executeBadData(data=data, item=self.owned.pk) data = {'access_policy': AccessPolicy.PRIVATE} - self.executeNotFound(data, item=self.invalid_item) - self.executeForbidden(data, item=self.unowned.pk) - self.executeOK(data, item=self.owned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeForbidden(data=data, item=self.unowned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.access_policy, data['access_policy']) self.toggle_editor(self.unowned, True) - self.executeForbidden(data, item=self.unowned.pk) + self.executeForbidden(data=data, item=self.unowned.pk) self.toggle_admin(True) - self.executeOK(data, item=self.unowned.pk) + self.executeOK(data=data, item=self.unowned.pk) self.unowned.refresh_from_db() self.assertEqual(self.unowned.access_policy, data['access_policy']) @@ -158,29 +157,29 @@ class TestLibraryViewset(EndpointTester): time_update = self.owned.time_update data = {'location': 'invalid'} - self.executeBadData(data, item=self.owned.pk) + self.executeBadData(data=data, item=self.owned.pk) data = {'location': '/U/temp'} - self.executeNotFound(data, item=self.invalid_item) - self.executeForbidden(data, item=self.unowned.pk) - self.executeOK(data, item=self.owned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeForbidden(data=data, item=self.unowned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.location, data['location']) data = {'location': LocationHead.LIBRARY} - self.executeForbidden(data, item=self.owned.pk) + self.executeForbidden(data=data, item=self.owned.pk) data = {'location': '/U/temp'} self.toggle_editor(self.unowned, True) - self.executeForbidden(data, item=self.unowned.pk) + self.executeForbidden(data=data, item=self.unowned.pk) self.toggle_admin(True) data = {'location': LocationHead.LIBRARY} - self.executeOK(data, item=self.owned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.location, data['location']) - self.executeOK(data, item=self.unowned.pk) + self.executeOK(data=data, item=self.unowned.pk) self.unowned.refresh_from_db() self.assertEqual(self.unowned.location, data['location']) @@ -189,22 +188,22 @@ class TestLibraryViewset(EndpointTester): time_update = self.owned.time_update data = {'user': self.invalid_user} - self.executeBadData(data, item=self.owned.pk) + self.executeBadData(data=data, item=self.owned.pk) data = {'user': self.user.pk} - self.executeNotFound(data, item=self.invalid_item) - self.executeForbidden(data, item=self.unowned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeForbidden(data=data, item=self.unowned.pk) - self.executeOK(data, item=self.owned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.time_update, time_update) self.assertEqual(self.owned.editors(), [self.user]) - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(self.owned.editors(), [self.user]) data = {'user': self.user2.pk} - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(set(self.owned.editors()), set([self.user, self.user2])) @@ -213,25 +212,25 @@ class TestLibraryViewset(EndpointTester): time_update = self.owned.time_update data = {'user': self.invalid_user} - self.executeBadData(data, item=self.owned.pk) + self.executeBadData(data=data, item=self.owned.pk) data = {'user': self.user.pk} - self.executeNotFound(data, item=self.invalid_item) - self.executeForbidden(data, item=self.unowned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeForbidden(data=data, item=self.unowned.pk) - self.executeOK(data, item=self.owned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.time_update, time_update) self.assertEqual(self.owned.editors(), []) Editor.add(item=self.owned, user=self.user) - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(self.owned.editors(), []) Editor.add(item=self.owned, user=self.user) Editor.add(item=self.owned, user=self.user2) data = {'user': self.user2.pk} - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(self.owned.editors(), [self.user]) @@ -240,30 +239,30 @@ class TestLibraryViewset(EndpointTester): time_update = self.owned.time_update data = {'users': [self.invalid_user]} - self.executeBadData(data, item=self.owned.pk) + self.executeBadData(data=data, item=self.owned.pk) data = {'users': [self.user.pk]} - self.executeNotFound(data, item=self.invalid_item) - self.executeForbidden(data, item=self.unowned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeForbidden(data=data, item=self.unowned.pk) - self.executeOK(data, item=self.owned.pk) + self.executeOK(data=data, item=self.owned.pk) self.owned.refresh_from_db() self.assertEqual(self.owned.time_update, time_update) self.assertEqual(self.owned.editors(), [self.user]) - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(self.owned.editors(), [self.user]) data = {'users': [self.user2.pk]} - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(self.owned.editors(), [self.user2]) data = {'users': []} - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(self.owned.editors(), []) data = {'users': [self.user2.pk, self.user.pk]} - self.executeOK(data) + self.executeOK(data=data) self.assertEqual(set(self.owned.editors()), set([self.user2, self.user])) @@ -294,7 +293,7 @@ class TestLibraryViewset(EndpointTester): @decl_endpoint('/api/library', method='get') def test_library_get(self): non_schema = LibraryItem.objects.create( - item_type=LibraryItemType.OPERATIONS_SCHEMA, + item_type=LibraryItemType.OPERATION_SCHEMA, title='Test4' ) response = self.executeOK() @@ -376,11 +375,11 @@ class TestLibraryViewset(EndpointTester): ) data = {'title': 'Title1337'} - self.executeNotFound(data, item=self.invalid_item) - self.executeCreated(data, item=self.unowned.pk) + self.executeNotFound(data=data, item=self.invalid_item) + self.executeCreated(data=data, item=self.unowned.pk) data = {'title': 'Title1338'} - response = self.executeCreated(data, item=self.owned.pk) + response = self.executeCreated(data=data, item=self.owned.pk) self.assertEqual(response.data['title'], data['title']) self.assertEqual(len(response.data['items']), 2) self.assertEqual(response.data['items'][0]['alias'], x12.alias) @@ -390,12 +389,12 @@ class TestLibraryViewset(EndpointTester): self.assertEqual(response.data['items'][1]['term_resolved'], d2.term_resolved) data = {'title': 'Title1340', 'items': []} - response = self.executeCreated(data, item=self.owned.pk) + response = self.executeCreated(data=data, item=self.owned.pk) self.assertEqual(response.data['title'], data['title']) self.assertEqual(len(response.data['items']), 0) data = {'title': 'Title1341', 'items': [x12.pk]} - response = self.executeCreated(data, item=self.owned.pk) + response = self.executeCreated(data=data, item=self.owned.pk) self.assertEqual(response.data['title'], data['title']) self.assertEqual(len(response.data['items']), 1) self.assertEqual(response.data['items'][0]['alias'], x12.alias) diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_operations.py b/rsconcept/backend/apps/rsform/tests/s_views/t_operations.py index 0cd9867f..e74d9422 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_operations.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_operations.py @@ -1,7 +1,6 @@ ''' Testing API: Operations. ''' from apps.rsform.models import Constituenta, CstType, RSForm - -from ..EndpointTester import EndpointTester, decl_endpoint +from shared.EndpointTester import EndpointTester, decl_endpoint class TestInlineSynthesis(EndpointTester): @@ -24,20 +23,20 @@ class TestInlineSynthesis(EndpointTester): 'items': [], 'substitutions': [] } - self.executeForbidden(data) + self.executeForbidden(data=data) data['receiver'] = invalid_id - self.executeBadData(data) + self.executeBadData(data=data) data['receiver'] = self.schema1.item.pk data['source'] = invalid_id - self.executeBadData(data) + self.executeBadData(data=data) data['source'] = self.schema1.item.pk - self.executeOK(data) + self.executeOK(data=data) data['items'] = [invalid_id] - self.executeBadData(data) + self.executeBadData(data=data) def test_inline_synthesis(self): @@ -68,7 +67,7 @@ class TestInlineSynthesis(EndpointTester): } ] } - response = self.executeOK(data) + response = self.executeOK(data=data) result = {item['alias']: item for item in response.data['items']} self.assertEqual(len(result), 6) self.assertEqual(result['X2']['term_raw'], ks1_x2.term_raw) diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py b/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py index dc735490..aa4ee1bc 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py @@ -15,9 +15,8 @@ from apps.rsform.models import ( LocationHead, RSForm ) - -from ..EndpointTester import EndpointTester, decl_endpoint -from ..testing_utils import response_contains +from shared.EndpointTester import EndpointTester, decl_endpoint +from shared.testing_utils import response_contains class TestRSFormViewset(EndpointTester): @@ -44,7 +43,7 @@ class TestRSFormViewset(EndpointTester): 'access_policy': AccessPolicy.PROTECTED, 'visible': False } - self.executeBadData(data) + self.executeBadData(data=data) with open(f'{work_dir}/data/sample-rsform.trs', 'rb') as file: data['file'] = file @@ -59,7 +58,7 @@ class TestRSFormViewset(EndpointTester): @decl_endpoint('/api/rsforms', method='get') def test_list_rsforms(self): non_schema = LibraryItem.objects.create( - item_type=LibraryItemType.OPERATIONS_SCHEMA, + item_type=LibraryItemType.OPERATION_SCHEMA, title='Test3' ) response = self.executeOK() @@ -124,14 +123,14 @@ class TestRSFormViewset(EndpointTester): def test_check(self): self.owned.insert_new('X1') data = {'expression': 'X1=X1'} - response = self.executeOK(data, item=self.owned_id) + response = self.executeOK(data=data, item=self.owned_id) self.assertEqual(response.data['parseResult'], True) self.assertEqual(response.data['syntax'], 'math') self.assertEqual(response.data['astText'], '[=[X1][X1]]') self.assertEqual(response.data['typification'], 'LOGIC') self.assertEqual(response.data['valueClass'], 'value') - self.executeOK(data, item=self.unowned_id) + self.executeOK(data=data, item=self.unowned_id) @decl_endpoint('/api/rsforms/{item}/resolve', method='post') @@ -142,7 +141,7 @@ class TestRSFormViewset(EndpointTester): ) data = {'text': '@{1|редкий} @{X1|plur,datv}'} - response = self.executeOK(data, item=self.owned_id) + response = self.executeOK(data=data, item=self.owned_id) self.assertEqual(response.data['input'], '@{1|редкий} @{X1|plur,datv}') self.assertEqual(response.data['output'], 'редким синим слонам') self.assertEqual(len(response.data['refs']), 2) @@ -189,13 +188,19 @@ class TestRSFormViewset(EndpointTester): @decl_endpoint('/api/rsforms/{item}/cst-create', method='post') def test_create_constituenta(self): - data = {'alias': 'X3', 'cst_type': CstType.BASE} - self.executeForbidden(data, item=self.unowned_id) + data = {'alias': 'X3'} + self.executeForbidden(data=data, item=self.unowned_id) self.owned.insert_new('X1') x2 = self.owned.insert_new('X2') + self.executeBadData(item=self.owned_id) + self.executeBadData(data=data, item=self.owned_id) - response = self.executeCreated(data, item=self.owned_id) + data['cst_type'] = 'invalid' + self.executeBadData(data=data, item=self.owned_id) + + data['cst_type'] = CstType.BASE + response = self.executeCreated(data=data, item=self.owned_id) self.assertEqual(response.data['new_cst']['alias'], 'X3') x3 = Constituenta.objects.get(alias=response.data['new_cst']['alias']) self.assertEqual(x3.order, 3) @@ -207,7 +212,7 @@ class TestRSFormViewset(EndpointTester): 'term_raw': 'test', 'term_forms': [{'text': 'form1', 'tags': 'sing,datv'}] } - response = self.executeCreated(data, item=self.owned_id) + response = self.executeCreated(data=data, item=self.owned_id) self.assertEqual(response.data['new_cst']['alias'], data['alias']) x4 = Constituenta.objects.get(alias=response.data['new_cst']['alias']) self.assertEqual(x4.order, 3) @@ -234,14 +239,14 @@ class TestRSFormViewset(EndpointTester): ) data = {'target': x2_2.pk, 'alias': 'D2', 'cst_type': CstType.TERM} - self.executeForbidden(data, item=self.unowned_id) - self.executeBadData(data, item=self.owned_id) + self.executeForbidden(data=data, item=self.unowned_id) + self.executeBadData(data=data, item=self.owned_id) data = {'target': x1.pk, 'alias': x1.alias, 'cst_type': CstType.TERM} - self.executeBadData(data, item=self.owned_id) + self.executeBadData(data=data, item=self.owned_id) data = {'target': x1.pk, 'alias': x3.alias} - self.executeBadData(data, item=self.owned_id) + self.executeBadData(data=data, item=self.owned_id) d1 = self.owned.insert_new( alias='D1', @@ -253,7 +258,7 @@ class TestRSFormViewset(EndpointTester): self.assertEqual(x1.cst_type, CstType.BASE) data = {'target': x1.pk, 'alias': 'D2', 'cst_type': CstType.TERM} - response = self.executeOK(data, item=self.owned_id) + response = self.executeOK(data=data, item=self.owned_id) self.assertEqual(response.data['new_cst']['alias'], 'D2') self.assertEqual(response.data['new_cst']['cst_type'], CstType.TERM) d1.refresh_from_db() @@ -280,14 +285,14 @@ class TestRSFormViewset(EndpointTester): unowned = self.unowned.insert_new('X2') data = {'substitutions': [{'original': x1.pk, 'substitution': unowned.pk, 'transfer_term': True}]} - self.executeForbidden(data, item=self.unowned_id) - self.executeBadData(data, item=self.owned_id) + self.executeForbidden(data=data, item=self.unowned_id) + self.executeBadData(data=data, item=self.owned_id) data = {'substitutions': [{'original': unowned.pk, 'substitution': x1.pk, 'transfer_term': True}]} - self.executeBadData(data, item=self.owned_id) + self.executeBadData(data=data, item=self.owned_id) data = {'substitutions': [{'original': x1.pk, 'substitution': x1.pk, 'transfer_term': True}]} - self.executeBadData(data, item=self.owned_id) + self.executeBadData(data=data, item=self.owned_id) d1 = self.owned.insert_new( alias='D1', @@ -295,7 +300,7 @@ class TestRSFormViewset(EndpointTester): definition_formal='X1' ) data = {'substitutions': [{'original': x1.pk, 'substitution': x2.pk, 'transfer_term': True}]} - response = self.executeOK(data, item=self.owned_id) + response = self.executeOK(data=data, item=self.owned_id) d1.refresh_from_db() x2.refresh_from_db() self.assertEqual(x2.term_raw, 'Test1') @@ -315,7 +320,7 @@ class TestRSFormViewset(EndpointTester): ) data = {'substitutions': []} - self.executeBadData(data) + self.executeBadData(data=data) data = {'substitutions': [ { @@ -329,7 +334,7 @@ class TestRSFormViewset(EndpointTester): 'transfer_term': True } ]} - self.executeBadData(data) + self.executeBadData(data=data) data = {'substitutions': [ { @@ -343,7 +348,7 @@ class TestRSFormViewset(EndpointTester): 'transfer_term': True } ]} - response = self.executeOK(data, item=self.owned_id) + response = self.executeOK(data=data, item=self.owned_id) d3.refresh_from_db() self.assertEqual(d3.definition_formal, r'D1 \ D2') @@ -358,7 +363,7 @@ class TestRSFormViewset(EndpointTester): 'definition_formal': '3', 'definition_raw': '4' } - response = self.executeCreated(data, item=self.owned_id) + response = self.executeCreated(data=data, item=self.owned_id) self.assertEqual(response.data['new_cst']['alias'], 'X3') self.assertEqual(response.data['new_cst']['cst_type'], CstType.BASE) self.assertEqual(response.data['new_cst']['convention'], '1') @@ -374,13 +379,13 @@ class TestRSFormViewset(EndpointTester): self.set_params(item=self.owned_id) data = {'items': [1337]} - self.executeBadData(data) + self.executeBadData(data=data) x1 = self.owned.insert_new('X1') x2 = self.owned.insert_new('X2') data = {'items': [x1.pk]} - response = self.executeOK(data) + response = self.executeOK(data=data) x2.refresh_from_db() self.owned.item.refresh_from_db() self.assertEqual(len(response.data['items']), 1) @@ -390,7 +395,7 @@ class TestRSFormViewset(EndpointTester): x3 = self.unowned.insert_new('X1') data = {'items': [x3.pk]} - self.executeBadData(data, item=self.owned_id) + self.executeBadData(data=data, item=self.owned_id) @decl_endpoint('/api/rsforms/{item}/cst-moveto', method='patch') @@ -398,13 +403,13 @@ class TestRSFormViewset(EndpointTester): self.set_params(item=self.owned_id) data = {'items': [1337], 'move_to': 1} - self.executeBadData(data) + self.executeBadData(data=data) x1 = self.owned.insert_new('X1') x2 = self.owned.insert_new('X2') data = {'items': [x2.pk], 'move_to': 1} - response = self.executeOK(data) + response = self.executeOK(data=data) x1.refresh_from_db() x2.refresh_from_db() self.assertEqual(response.data['id'], self.owned_id) @@ -413,7 +418,7 @@ class TestRSFormViewset(EndpointTester): x3 = self.unowned.insert_new('X1') data = {'items': [x3.pk], 'move_to': 1} - self.executeBadData(data) + self.executeBadData(data=data) @decl_endpoint('/api/rsforms/{item}/reset-aliases', method='patch') diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_rslang.py b/rsconcept/backend/apps/rsform/tests/s_views/t_rslang.py index 910e852e..c35cf491 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_rslang.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_rslang.py @@ -1,5 +1,5 @@ ''' Testing views ''' -from ..EndpointTester import EndpointTester, decl_endpoint +from shared.EndpointTester import EndpointTester, decl_endpoint class TestRSLanguageViews(EndpointTester): @@ -8,30 +8,30 @@ class TestRSLanguageViews(EndpointTester): @decl_endpoint('/api/rslang/to-ascii', method='post') def test_convert_to_ascii(self): data = {'data': '1=1'} - self.executeBadData(data) + self.executeBadData(data=data) data = {'expression': '1=1'} - response = self.executeOK(data) + response = self.executeOK(data=data) self.assertEqual(response.data['result'], r'1 \eq 1') @decl_endpoint('/api/rslang/to-math', method='post') def test_convert_to_math(self): data = {'data': r'1 \eq 1'} - self.executeBadData(data) + self.executeBadData(data=data) data = {'expression': r'1 \eq 1'} - response = self.executeOK(data) + response = self.executeOK(data=data) self.assertEqual(response.data['result'], r'1=1') @decl_endpoint('/api/rslang/parse-expression', method='post') def test_parse_expression(self): data = {'data': r'1=1'} - self.executeBadData(data) + self.executeBadData(data=data) data = {'expression': r'1=1'} - response = self.executeOK(data) + response = self.executeOK(data=data) self.assertEqual(response.data['parseResult'], True) self.assertEqual(response.data['syntax'], 'math') self.assertEqual(response.data['astText'], '[=[1][1]]') diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_versions.py b/rsconcept/backend/apps/rsform/tests/s_views/t_versions.py index d2b273a7..cc8433cd 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_versions.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_versions.py @@ -7,8 +7,7 @@ from zipfile import ZipFile from rest_framework import status from apps.rsform.models import Constituenta, RSForm - -from ..EndpointTester import EndpointTester, decl_endpoint +from shared.EndpointTester import EndpointTester, decl_endpoint class TestVersionViews(EndpointTester): @@ -31,11 +30,11 @@ class TestVersionViews(EndpointTester): invalid_id = 1338 data = {'version': '1.0.0', 'description': 'test'} - self.executeNotFound(data, schema=invalid_id) - self.executeForbidden(data, schema=self.unowned.pk) - self.executeBadData(invalid_data, schema=self.owned.pk) + self.executeNotFound(data=data, schema=invalid_id) + self.executeForbidden(data=data, schema=self.unowned.pk) + self.executeBadData(data=invalid_data, schema=self.owned.pk) - response = self.executeCreated(data, schema=self.owned.pk) + response = self.executeCreated(data=data, schema=self.owned.pk) self.assertTrue('version' in response.data) self.assertTrue('schema' in response.data) self.assertTrue(response.data['version'] in [v['id'] for v in response.data['schema']['versions']]) @@ -65,7 +64,7 @@ class TestVersionViews(EndpointTester): @decl_endpoint('/api/versions/{version}', method='get') def test_access_version(self): data = {'version': '1.0.0', 'description': 'test'} - version_id = self._create_version(data) + version_id = self._create_version(data=data) invalid_id = version_id + 1337 self.executeNotFound(version=invalid_id) @@ -79,14 +78,14 @@ class TestVersionViews(EndpointTester): data = {'version': '1.2.0', 'description': 'test1'} self.method = 'patch' - self.executeForbidden(data) + self.executeForbidden(data=data) self.method = 'delete' self.executeForbidden() self.client.force_authenticate(user=self.user) self.method = 'patch' - self.executeOK(data) + self.executeOK(data=data) response = self.get() self.assertEqual(response.data['version'], data['version']) self.assertEqual(response.data['description'], data['description']) @@ -139,7 +138,7 @@ class TestVersionViews(EndpointTester): x2 = self.schema.insert_new('X2') d1 = self.schema.insert_new('D1', term_raw='TestTerm') data = {'version': '1.0.0', 'description': 'test'} - version_id = self._create_version(data) + version_id = self._create_version(data=data) invalid_id = version_id + 1337 d1.delete() diff --git a/rsconcept/backend/apps/rsform/views/constituents.py b/rsconcept/backend/apps/rsform/views/constituents.py index cd772f1c..195a099c 100644 --- a/rsconcept/backend/apps/rsform/views/constituents.py +++ b/rsconcept/backend/apps/rsform/views/constituents.py @@ -2,8 +2,9 @@ from drf_spectacular.utils import extend_schema, extend_schema_view from rest_framework import generics +from shared import permissions + from .. import models as m -from .. import permissions from .. import serializers as s diff --git a/rsconcept/backend/apps/rsform/views/library.py b/rsconcept/backend/apps/rsform/views/library.py index 4ce592af..4748a7d8 100644 --- a/rsconcept/backend/apps/rsform/views/library.py +++ b/rsconcept/backend/apps/rsform/views/library.py @@ -13,8 +13,9 @@ from rest_framework.decorators import action from rest_framework.request import Request from rest_framework.response import Response +from shared import permissions + from .. import models as m -from .. import permissions from .. import serializers as s @@ -42,17 +43,27 @@ class LibraryViewSet(viewsets.ModelViewSet): def get_permissions(self): if self.action in ['update', 'partial_update']: - permission_list = [permissions.ItemEditor] + access_level = permissions.ItemEditor elif self.action in [ - 'destroy', 'set_owner', 'set_access_policy', 'set_location', - 'editors_add', 'editors_remove', 'editors_set' + 'destroy', + 'set_owner', + 'set_access_policy', + 'set_location', + 'editors_add', + 'editors_remove', + 'editors_set' ]: - permission_list = [permissions.ItemOwner] - elif self.action in ['create', 'clone', 'subscribe', 'unsubscribe']: - permission_list = [permissions.GlobalUser] + access_level = permissions.ItemOwner + elif self.action in [ + 'create', + 'clone', + 'subscribe', + 'unsubscribe' + ]: + access_level = permissions.GlobalUser else: - permission_list = [permissions.ItemAnyone] - return [permission() for permission in permission_list] + access_level = permissions.ItemAnyone + return [access_level()] def _get_item(self) -> m.LibraryItem: return cast(m.LibraryItem, self.get_object()) @@ -68,7 +79,6 @@ class LibraryViewSet(viewsets.ModelViewSet): c.HTTP_404_NOT_FOUND: None } ) - @transaction.atomic @action(detail=True, methods=['post'], url_path='clone') def clone(self, request: Request, pk): ''' Endpoint: Create deep copy of library item. ''' @@ -85,19 +95,20 @@ class LibraryViewSet(viewsets.ModelViewSet): clone.read_only = False clone.access_policy = serializer.validated_data.get('access_policy', m.AccessPolicy.PUBLIC) clone.location = serializer.validated_data.get('location', m.LocationHead.USER) - clone.save() - if clone.item_type == m.LibraryItemType.RSFORM: - need_filter = 'items' in request.data - for cst in m.RSForm(item).constituents(): - if not need_filter or cst.pk in request.data['items']: - cst.pk = None - cst.schema = clone - cst.save() - return Response( - status=c.HTTP_201_CREATED, - data=s.RSFormParseSerializer(clone).data - ) + with transaction.atomic(): + clone.save() + if clone.item_type == m.LibraryItemType.RSFORM: + need_filter = 'items' in request.data + for cst in m.RSForm(item).constituents(): + if not need_filter or cst.pk in request.data['items']: + cst.pk = None + cst.schema = clone + cst.save() + return Response( + status=c.HTTP_201_CREATED, + data=s.RSFormParseSerializer(clone).data + ) return Response(status=c.HTTP_400_BAD_REQUEST) @extend_schema( @@ -266,24 +277,17 @@ class LibraryActiveView(generics.ListAPIView): serializer_class = s.LibraryItemSerializer def get_queryset(self): + common_location = Q(location__startswith=m.LocationHead.COMMON) | Q(location__startswith=m.LocationHead.LIBRARY) + is_public = Q(access_policy=m.AccessPolicy.PUBLIC) if self.request.user.is_anonymous: - return m.LibraryItem.objects.filter( - Q(access_policy=m.AccessPolicy.PUBLIC), - ).filter( - Q(location__startswith=m.LocationHead.COMMON) | - Q(location__startswith=m.LocationHead.LIBRARY) - ).order_by('-time_update') + return m.LibraryItem.objects \ + .filter(is_public) \ + .filter(common_location).order_by('-time_update') else: user = cast(m.User, self.request.user) # pylint: disable=unsupported-binary-operation return m.LibraryItem.objects.filter( - ( - Q(access_policy=m.AccessPolicy.PUBLIC) & - ( - Q(location__startswith=m.LocationHead.COMMON) | - Q(location__startswith=m.LocationHead.LIBRARY) - ) - ) | + (is_public & common_location) | Q(owner=user) | Q(editor__editor=user) | Q(subscription__user=user) diff --git a/rsconcept/backend/apps/rsform/views/operations.py b/rsconcept/backend/apps/rsform/views/operations.py index 4520d29f..3dca6751 100644 --- a/rsconcept/backend/apps/rsform/views/operations.py +++ b/rsconcept/backend/apps/rsform/views/operations.py @@ -18,7 +18,6 @@ from .. import serializers as s request=s.InlineSynthesisSerializer, responses={c.HTTP_200_OK: s.RSFormParseSerializer} ) -@transaction.atomic @api_view(['PATCH']) def inline_synthesis(request: Request): ''' Endpoint: Inline synthesis. ''' @@ -30,20 +29,21 @@ def inline_synthesis(request: Request): schema = m.RSForm(serializer.validated_data['receiver']) items = cast(list[m.Constituenta], serializer.validated_data['items']) - new_items = schema.insert_copy(items) - for substitution in serializer.validated_data['substitutions']: - original = cast(m.Constituenta, substitution['original']) - replacement = cast(m.Constituenta, substitution['substitution']) - if original in items: - index = next(i for (i, cst) in enumerate(items) if cst == original) - original = new_items[index] - else: - index = next(i for (i, cst) in enumerate(items) if cst == replacement) - replacement = new_items[index] - schema.substitute(original, replacement, substitution['transfer_term']) + with transaction.atomic(): + new_items = schema.insert_copy(items) + for substitution in serializer.validated_data['substitutions']: + original = cast(m.Constituenta, substitution['original']) + replacement = cast(m.Constituenta, substitution['substitution']) + if original in items: + index = next(i for (i, cst) in enumerate(items) if cst == original) + original = new_items[index] + else: + index = next(i for (i, cst) in enumerate(items) if cst == replacement) + replacement = new_items[index] + schema.substitute(original, replacement, substitution['transfer_term']) + schema.restore_order() - schema.restore_order() return Response( status=c.HTTP_200_OK, data=s.RSFormParseSerializer(schema.item).data diff --git a/rsconcept/backend/apps/rsform/views/rsforms.py b/rsconcept/backend/apps/rsform/views/rsforms.py index 1af946f1..5c3a4e9a 100644 --- a/rsconcept/backend/apps/rsform/views/rsforms.py +++ b/rsconcept/backend/apps/rsform/views/rsforms.py @@ -13,9 +13,10 @@ from rest_framework.decorators import action, api_view from rest_framework.request import Request from rest_framework.response import Response -from .. import messages as msg +from shared import messages as msg +from shared import permissions + from .. import models as m -from .. import permissions from .. import serializers as s from .. import utils @@ -33,11 +34,21 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr def get_permissions(self): ''' Determine permission class. ''' if self.action in [ - 'load_trs', 'cst_create', 'cst_delete_multiple', - 'reset_aliases', 'cst_rename', 'cst_substitute' + 'load_trs', + 'reset_aliases', + 'cst_create', + 'cst_delete_multiple', + 'cst_rename', + 'cst_substitute' ]: permission_list = [permissions.ItemEditor] - elif self.action in ['contents', 'details', 'export_trs', 'resolve', 'check']: + elif self.action in [ + 'contents', + 'details', + 'export_trs', + 'resolve', + 'check' + ]: permission_list = [permissions.ItemAnyone] else: permission_list = [permissions.Anyone] @@ -49,6 +60,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr request=s.CstCreateSerializer, responses={ c.HTTP_201_CREATED: s.NewCstResponse, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } @@ -60,10 +72,15 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr serializer = s.CstCreateSerializer(data=request.data) serializer.is_valid(raise_exception=True) data = serializer.validated_data - new_cst = schema.create_cst( - data=data, - insert_after=data['insert_after'] if 'insert_after' in data else None - ) + if 'insert_after' in data: + try: + insert_after = m.Constituenta.objects.get(pk=data['insert_after']) + except m.LibraryItem.DoesNotExist: + return Response(status=c.HTTP_404_NOT_FOUND) + else: + insert_after = None + new_cst = schema.create_cst(data, insert_after) + schema.item.refresh_from_db() response = Response( status=c.HTTP_201_CREATED, @@ -81,6 +98,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr request=s.CstTargetSerializer, responses={ c.HTTP_200_OK: s.NewMultiCstResponse, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } @@ -117,11 +135,11 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr request=s.CstRenameSerializer, responses={ c.HTTP_200_OK: s.NewCstResponse, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } ) - @transaction.atomic @action(detail=True, methods=['patch'], url_path='cst-rename') def cst_rename(self, request: Request, pk): ''' Rename constituenta possibly changing type. ''' @@ -134,12 +152,12 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr cst.alias = serializer.validated_data['alias'] cst.cst_type = serializer.validated_data['cst_type'] - cst.save() - mapping = {old_alias: cst.alias} - schema.apply_mapping(mapping, change_aliases=False) - schema.item.refresh_from_db() - cst.refresh_from_db() + with transaction.atomic(): + cst.save() + schema.apply_mapping(mapping={old_alias: cst.alias}, change_aliases=False) + schema.item.refresh_from_db() + cst.refresh_from_db() return Response( status=c.HTTP_200_OK, @@ -155,11 +173,11 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr request=s.CstSubstituteSerializer, responses={ c.HTTP_200_OK: s.RSFormParseSerializer, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } ) - @transaction.atomic @action(detail=True, methods=['patch'], url_path='cst-substitute') def cst_substitute(self, request: Request, pk): ''' Substitute occurrences of constituenta with another one. ''' @@ -169,11 +187,13 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr context={'schema': schema.item} ) serializer.is_valid(raise_exception=True) - for substitution in serializer.validated_data['substitutions']: - original = cast(m.Constituenta, substitution['original']) - replacement = cast(m.Constituenta, substitution['substitution']) - schema.substitute(original, replacement, substitution['transfer_term']) - schema.item.refresh_from_db() + + with transaction.atomic(): + for substitution in serializer.validated_data['substitutions']: + original = cast(m.Constituenta, substitution['original']) + replacement = cast(m.Constituenta, substitution['substitution']) + schema.substitute(original, replacement, substitution['transfer_term']) + schema.item.refresh_from_db() return Response( status=c.HTTP_200_OK, data=s.RSFormParseSerializer(schema.item).data @@ -185,6 +205,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr request=s.CstListSerializer, responses={ c.HTTP_200_OK: s.RSFormParseSerializer, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } @@ -211,6 +232,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr request=s.CstMoveSerializer, responses={ c.HTTP_200_OK: s.RSFormParseSerializer, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } @@ -279,6 +301,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr request=s.RSFormUploadSerializer, responses={ c.HTTP_200_OK: s.RSFormParseSerializer, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } @@ -313,7 +336,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None } ) - @action(detail=True, methods=['get']) + @action(detail=True, methods=['get'], url_path='contents') def contents(self, request: Request, pk): ''' Endpoint: View schema db contents (including constituents). ''' schema = s.RSFormSerializer(self.get_object()) @@ -331,7 +354,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None } ) - @action(detail=True, methods=['get']) + @action(detail=True, methods=['get'], url_path='details') def details(self, request: Request, pk): ''' Endpoint: Detailed schema view including statuses and parse. ''' serializer = s.RSFormParseSerializer(cast(m.LibraryItem, self.get_object())) @@ -349,7 +372,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None }, ) - @action(detail=True, methods=['post']) + @action(detail=True, methods=['post'], url_path='check') def check(self, request: Request, pk): ''' Endpoint: Check RSLang expression against schema context. ''' serializer = s.ExpressionSerializer(data=request.data) @@ -371,7 +394,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr c.HTTP_404_NOT_FOUND: None } ) - @action(detail=True, methods=['post']) + @action(detail=True, methods=['post'], url_path='resolve') def resolve(self, request: Request, pk): ''' Endpoint: Resolve references in text against schema terms context. ''' serializer = s.TextSerializer(data=request.data) diff --git a/rsconcept/backend/apps/rsform/views/versions.py b/rsconcept/backend/apps/rsform/views/versions.py index a5e69c9a..318e4da5 100644 --- a/rsconcept/backend/apps/rsform/views/versions.py +++ b/rsconcept/backend/apps/rsform/views/versions.py @@ -10,8 +10,9 @@ from rest_framework.decorators import action, api_view, permission_classes from rest_framework.request import Request from rest_framework.response import Response +from shared import permissions + from .. import models as m -from .. import permissions from .. import serializers as s from .. import utils @@ -54,6 +55,7 @@ class VersionViewset( request=s.VersionCreateSerializer, responses={ c.HTTP_201_CREATED: s.NewVersionResponse, + c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None } diff --git a/rsconcept/backend/apps/users/messages.py b/rsconcept/backend/apps/users/messages.py deleted file mode 100644 index f142f37a..00000000 --- a/rsconcept/backend/apps/users/messages.py +++ /dev/null @@ -1,14 +0,0 @@ -''' Utility: Text messages. ''' -# pylint: skip-file - - -def passwordAuthFailed(): - return 'Неизвестное сочетание имени пользователя (email) и пароля' - - -def passwordsNotMatch(): - return 'Введенные пароли не совпадают' - - -def emailAlreadyTaken(): - return 'Пользователь с данным email уже существует' diff --git a/rsconcept/backend/apps/users/serializers.py b/rsconcept/backend/apps/users/serializers.py index d7ed3dd6..31f9c2e0 100644 --- a/rsconcept/backend/apps/users/serializers.py +++ b/rsconcept/backend/apps/users/serializers.py @@ -4,8 +4,8 @@ from django.contrib.auth.password_validation import validate_password from rest_framework import serializers from apps.rsform.models import Editor, Subscription +from shared import messages as msg -from . import messages as msg from . import models diff --git a/rsconcept/backend/apps/users/tests/t_views.py b/rsconcept/backend/apps/users/tests/t_views.py index 44551a5c..d289cc91 100644 --- a/rsconcept/backend/apps/users/tests/t_views.py +++ b/rsconcept/backend/apps/users/tests/t_views.py @@ -1,30 +1,30 @@ ''' Testing API: users. ''' from rest_framework.test import APIClient, APITestCase -from apps.rsform.tests.EndpointTester import EndpointTester, decl_endpoint from apps.users.models import User +from shared.EndpointTester import EndpointTester, decl_endpoint class TestUserAPIViews(EndpointTester): ''' Testing Authentication views. ''' def setUp(self): - super().setUp() + super().setUpFullUsers() @decl_endpoint('/users/api/login', method='post') def test_login(self): self.logout() data = {'username': self.user.username, 'password': 'invalid'} - self.executeBadData(data) + self.executeBadData(data=data) data = {'username': self.user.username, 'password': 'password'} - self.executeAccepted(data) - self.executeAccepted(data) + self.executeAccepted(data=data) + self.executeAccepted(data=data) self.logout() data = {'username': self.user.email, 'password': 'password'} - self.executeAccepted(data) + self.executeAccepted(data=data) @decl_endpoint('/users/api/logout', method='post') @@ -59,7 +59,7 @@ class TestUserUserProfileAPIView(EndpointTester): ''' Testing User profile views. ''' def setUp(self): - super().setUp() + super().setUpFullUsers() self.user.first_name = 'John' self.user.second_name = 'Smith' self.user.save() @@ -84,7 +84,7 @@ class TestUserUserProfileAPIView(EndpointTester): 'first_name': 'firstName', 'last_name': 'lastName', } - response = self.executeOK(data) + response = self.executeOK(data=data) self.user.refresh_from_db() self.assertEqual(response.data['email'], '123@mail.ru') self.assertEqual(self.user.email, '123@mail.ru') @@ -98,10 +98,10 @@ class TestUserUserProfileAPIView(EndpointTester): 'first_name': 'new', 'last_name': 'new2', } - self.executeOK(data) + self.executeOK(data=data) data = {'email': self.user2.email} - self.executeBadData(data) + self.executeBadData(data=data) self.logout() self.executeForbidden() @@ -113,14 +113,14 @@ class TestUserUserProfileAPIView(EndpointTester): 'old_password': 'invalid', 'new_password': 'password2' } - self.executeBadData(data) + self.executeBadData(data=data) data = { 'old_password': 'password', 'new_password': 'password2' } oldHash = self.user.password - response = self.executeNoContent(data) + response = self.executeNoContent(data=data) self.user.refresh_from_db() self.assertNotEqual(self.user.password, oldHash) self.assertTrue(self.client.login(username=self.user.username, password='password2')) @@ -154,7 +154,7 @@ class TestSignupAPIView(EndpointTester): 'first_name': 'firstName', 'last_name': 'lastName' } - self.executeBadData(data) + self.executeBadData(data=data) data = { 'username': 'NewUser', @@ -164,7 +164,7 @@ class TestSignupAPIView(EndpointTester): 'first_name': 'firstName', 'last_name': 'lastName' } - response = self.executeCreated(data) + response = self.executeCreated(data=data) self.assertTrue('id' in response.data) self.assertEqual(response.data['username'], data['username']) self.assertEqual(response.data['email'], data['email']) @@ -179,7 +179,7 @@ class TestSignupAPIView(EndpointTester): 'first_name': 'firstName', 'last_name': 'lastName' } - self.executeBadData(data) + self.executeBadData(data=data) data = { 'username': 'NewUser2', @@ -189,4 +189,4 @@ class TestSignupAPIView(EndpointTester): 'first_name': 'firstName', 'last_name': 'lastName' } - self.executeBadData(data) + self.executeBadData(data=data) diff --git a/rsconcept/backend/project/settings.py b/rsconcept/backend/project/settings.py index 2d2f0de2..5357c542 100644 --- a/rsconcept/backend/project/settings.py +++ b/rsconcept/backend/project/settings.py @@ -74,6 +74,7 @@ INSTALLED_APPS = [ 'apps.users', 'apps.rsform', + 'apps.oss', 'drf_spectacular', 'drf_spectacular_sidecar', diff --git a/rsconcept/backend/project/urls.py b/rsconcept/backend/project/urls.py index 550874ba..c6c2aa7e 100644 --- a/rsconcept/backend/project/urls.py +++ b/rsconcept/backend/project/urls.py @@ -9,6 +9,7 @@ from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, Spec urlpatterns = [ path('admin', admin.site.urls), path('api/', include('apps.rsform.urls')), + path('api/', include('apps.oss.urls')), path('users/', include('apps.users.urls')), path('schema', SpectacularAPIView.as_view(), name='schema'), path('redoc', SpectacularRedocView.as_view()), diff --git a/rsconcept/backend/requirements-dev.txt b/rsconcept/backend/requirements-dev.txt index ab9d5484..a33d4026 100644 --- a/rsconcept/backend/requirements-dev.txt +++ b/rsconcept/backend/requirements-dev.txt @@ -14,7 +14,6 @@ psycopg2-binary gunicorn djangorestframework-stubs[compatible-mypy] - mypy pylint coverage \ No newline at end of file diff --git a/rsconcept/backend/requirements.txt b/rsconcept/backend/requirements.txt index 8fdafb21..652b2571 100644 --- a/rsconcept/backend/requirements.txt +++ b/rsconcept/backend/requirements.txt @@ -1,13 +1,13 @@ tzdata==2024.1 -Django==5.0.6 -djangorestframework==3.15.1 -django-cors-headers==4.3.1 +Django==5.0.7 +djangorestframework==3.15.2 +django-cors-headers==4.4.0 django-filter==24.2 drf-spectacular==0.27.2 -drf-spectacular-sidecar==2024.6.1 +drf-spectacular-sidecar==2024.7.1 coreapi==2.3.3 django-rest-passwordreset==1.4.1 -cctext==0.1.3 +cctext==0.1.4 pyconcept==0.1.6 psycopg2-binary==2.9.9 diff --git a/rsconcept/backend/apps/rsform/tests/EndpointTester.py b/rsconcept/backend/shared/EndpointTester.py similarity index 92% rename from rsconcept/backend/apps/rsform/tests/EndpointTester.py rename to rsconcept/backend/shared/EndpointTester.py index e2436cc4..0f0a6b63 100644 --- a/rsconcept/backend/apps/rsform/tests/EndpointTester.py +++ b/rsconcept/backend/shared/EndpointTester.py @@ -26,6 +26,21 @@ class EndpointTester(APITestCase): ''' Abstract base class for Testing endpoints. ''' def setUp(self): + self.factory = APIRequestFactory() + self.user = User.objects.create( + username='UserTest', + email='blank@test.com', + password='password' + ) + self.user2 = User.objects.create( + username='UserTest2', + email='another@test.com', + password='password' + ) + self.client = APIClient() + self.client.force_authenticate(user=self.user) + + def setUpFullUsers(self): self.factory = APIRequestFactory() self.user = User.objects.create_user( username='UserTest', diff --git a/rsconcept/backend/shared/__init__.py b/rsconcept/backend/shared/__init__.py new file mode 100644 index 00000000..6246926d --- /dev/null +++ b/rsconcept/backend/shared/__init__.py @@ -0,0 +1 @@ +''' Utilities shared between applications. ''' diff --git a/rsconcept/backend/apps/rsform/messages.py b/rsconcept/backend/shared/messages.py similarity index 79% rename from rsconcept/backend/apps/rsform/messages.py rename to rsconcept/backend/shared/messages.py index 9469bbdb..dae5328e 100644 --- a/rsconcept/backend/apps/rsform/messages.py +++ b/rsconcept/backend/shared/messages.py @@ -6,6 +6,10 @@ def constituentaNotOwned(title: str): return f'Конституента не принадлежит схеме: {title}' +def operationNotOwned(title: str): + return f'Операция не принадлежит схеме: {title}' + + def substitutionNotInList(): return 'Отождествляемая конституента отсутствует в списке' @@ -64,3 +68,15 @@ def constituentaNoStructure(): def missingFile(): return 'Отсутствует прикрепленный файл' + + +def passwordAuthFailed(): + return 'Неизвестное сочетание имени пользователя (email) и пароля' + + +def passwordsNotMatch(): + return 'Введенные пароли не совпадают' + + +def emailAlreadyTaken(): + return 'Пользователь с данным email уже существует' diff --git a/rsconcept/backend/apps/rsform/permissions.py b/rsconcept/backend/shared/permissions.py similarity index 78% rename from rsconcept/backend/apps/rsform/permissions.py rename to rsconcept/backend/shared/permissions.py index 7a163dec..87726c4d 100644 --- a/rsconcept/backend/apps/rsform/permissions.py +++ b/rsconcept/backend/shared/permissions.py @@ -11,16 +11,27 @@ from rest_framework.permissions import \ from rest_framework.request import Request from rest_framework.views import APIView -from . import models as m +from apps.oss.models import Operation +from apps.rsform.models import ( + AccessPolicy, + Constituenta, + Editor, + LibraryItem, + Subscription, + Version +) +from apps.users.models import User -def _extract_item(obj: Any) -> m.LibraryItem: - if isinstance(obj, m.LibraryItem): +def _extract_item(obj: Any) -> LibraryItem: + if isinstance(obj, LibraryItem): return obj - elif isinstance(obj, m.Constituenta): - return cast(m.LibraryItem, obj.schema) - elif isinstance(obj, (m.Version, m.Subscription, m.Editor)): - return cast(m.LibraryItem, obj.item) + elif isinstance(obj, Constituenta): + return cast(LibraryItem, obj.schema) + elif isinstance(obj, Operation): + return cast(LibraryItem, obj.oss) + elif isinstance(obj, (Version, Subscription, Editor)): + return cast(LibraryItem, obj.item) raise PermissionDenied({ 'message': 'Invalid type error. Please contact developers', 'object_id': obj.id @@ -60,10 +71,10 @@ class ItemEditor(ItemOwner): if request.user.is_anonymous: return False item = _extract_item(obj) - if m.Editor.objects.filter( + if Editor.objects.filter( item=item, - editor=cast(m.User, request.user) - ).exists() and item.access_policy != m.AccessPolicy.PRIVATE: + editor=cast(User, request.user) + ).exists() and item.access_policy != AccessPolicy.PRIVATE: return True return super().has_object_permission(request, view, obj) @@ -76,7 +87,7 @@ class ItemAnyone(ItemEditor): def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool: item = _extract_item(obj) - if item.access_policy == m.AccessPolicy.PUBLIC: + if item.access_policy == AccessPolicy.PUBLIC: return True return super().has_object_permission(request, view, obj) diff --git a/rsconcept/backend/apps/rsform/tests/testing_utils.py b/rsconcept/backend/shared/testing_utils.py similarity index 100% rename from rsconcept/backend/apps/rsform/tests/testing_utils.py rename to rsconcept/backend/shared/testing_utils.py diff --git a/rsconcept/frontend/.env.local b/rsconcept/frontend/.env.local index e406b9c7..114223fa 100644 --- a/rsconcept/frontend/.env.local +++ b/rsconcept/frontend/.env.local @@ -1,5 +1,4 @@ # Local build config VITE_PORTAL_BACKEND=http://localhost:8000 -VITE_PORTAL_FRONT_PORT=3000 -VITE_PORTAL_FRONT_HTTPS=false +VITE_PORTAL_FRONT_PORT=3000 \ No newline at end of file diff --git a/rsconcept/frontend/Dockerfile b/rsconcept/frontend/Dockerfile index fdff3fce..6cba1d87 100644 --- a/rsconcept/frontend/Dockerfile +++ b/rsconcept/frontend/Dockerfile @@ -1,26 +1,30 @@ # ======== Multi-stage base ========== -FROM node:bullseye-slim as node-base +FROM node:22-bookworm-slim AS node-base RUN apt-get update -qq && \ apt-get upgrade -y && \ rm -rf /var/lib/apt/lists/* # ======= Build ======= ARG BUILD_TYPE=production -FROM node-base as builder +FROM node-base AS builder WORKDIR /result +RUN npm install -g typescript vite + COPY ./ ./ COPY ./env/.env.$BUILD_TYPE ./ RUN rm -rf ./env + RUN npm ci -ENV NODE_ENV production + +ENV NODE_ENV=production RUN npm run build # ========= Server ======= -FROM node-base as product-server +FROM node-base AS product-server -ENV NODE_ENV production +ENV NODE_ENV=production # Install serve util RUN npm install -g serve diff --git a/rsconcept/frontend/Dockerfile.dev b/rsconcept/frontend/Dockerfile.dev index be4e8c9f..00e36bab 100644 --- a/rsconcept/frontend/Dockerfile.dev +++ b/rsconcept/frontend/Dockerfile.dev @@ -1,14 +1,17 @@ # ======== Multi-stage base ========== -FROM node:bullseye-slim as node-base +FROM node:22-bookworm-slim AS node-base RUN apt-get update -qq && \ apt-get upgrade -y && \ rm -rf /var/lib/apt/lists/* # ========= Server ======= -FROM node-base as product-server -ARG BUILD_TYPE=production +FROM node-base AS product-server +ARG BUILD_TYPE=development + +ENV NODE_ENV=development WORKDIR /home +RUN npm install -g vite COPY ./ ./ COPY ./env/.env.$BUILD_TYPE ./ diff --git a/rsconcept/frontend/env/.env.development b/rsconcept/frontend/env/.env.development index b662d21b..9e83a3a4 100644 --- a/rsconcept/frontend/env/.env.development +++ b/rsconcept/frontend/env/.env.development @@ -2,4 +2,3 @@ VITE_PORTAL_BACKEND=http://localhost:8002 VITE_PORTAL_FRONT_PORT=3002 -VITE_PORTAL_FRONT_HTTPS=false diff --git a/rsconcept/frontend/env/.env.production b/rsconcept/frontend/env/.env.production index 7d450ce8..2f98a22f 100644 --- a/rsconcept/frontend/env/.env.production +++ b/rsconcept/frontend/env/.env.production @@ -2,4 +2,3 @@ VITE_PORTAL_BACKEND=https://api.portal.acconcept.ru VITE_PORTAL_FRONT_PORT=443 -VITE_PORTAL_FRONT_HTTPS=true diff --git a/rsconcept/frontend/env/.env.production.local b/rsconcept/frontend/env/.env.production.local index afc1a660..c373d02c 100644 --- a/rsconcept/frontend/env/.env.production.local +++ b/rsconcept/frontend/env/.env.production.local @@ -2,5 +2,4 @@ VITE_PORTAL_BACKEND=https://localhost:8001 VITE_PORTAL_FRONT_PORT=3001 -VITE_PORTAL_FRONT_HTTPS=true diff --git a/rsconcept/frontend/package-lock.json b/rsconcept/frontend/package-lock.json index 0aefa717..d3fff291 100644 --- a/rsconcept/frontend/package-lock.json +++ b/rsconcept/frontend/package-lock.json @@ -9,50 +9,50 @@ "version": "1.0.0", "dependencies": { "@lezer/lr": "^1.4.1", - "@reactflow/core": "^11.11.4", - "@tanstack/react-table": "^8.17.3", - "@uiw/codemirror-themes": "^4.22.2", - "@uiw/react-codemirror": "^4.22.2", + "@tanstack/react-table": "^8.19.3", + "@uiw/codemirror-themes": "^4.23.0", + "@uiw/react-codemirror": "^4.23.0", "axios": "^1.7.2", "clsx": "^2.1.1", - "framer-motion": "^10.18.0", + "framer-motion": "^11.3.8", "js-file-download": "^0.4.12", "react": "^18.3.1", "react-dom": "^18.3.1", "react-error-boundary": "^4.0.13", - "react-icons": "^4.12.0", + "react-icons": "^5.2.1", "react-intl": "^6.6.8", - "react-loader-spinner": "^5.4.5", - "react-pdf": "^9.0.0", - "react-router-dom": "^6.23.1", + "react-loader-spinner": "^6.1.6", + "react-pdf": "^9.1.0", + "react-router-dom": "^6.25.1", "react-select": "^5.8.0", "react-tabs": "^6.0.2", - "react-toastify": "^9.1.3", - "react-tooltip": "^5.27.0", - "reagraph": "^4.19.1", + "react-toastify": "^10.0.5", + "react-tooltip": "^5.27.1", + "reactflow": "^11.11.4", + "reagraph": "^4.19.2", "use-debounce": "^10.0.1" }, "devDependencies": { "@lezer/generator": "^1.7.1", "@types/jest": "^29.5.12", - "@types/node": "^20.14.7", + "@types/node": "^20.14.11", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.16.1", + "@typescript-eslint/parser": "^7.16.1", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.19", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.7", - "eslint-plugin-simple-import-sort": "^10.0.0", - "eslint-plugin-tsdoc": "^0.2.17", + "eslint-plugin-react-refresh": "^0.4.8", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-tsdoc": "^0.3.0", "jest": "^29.7.0", - "postcss": "^8.4.38", - "tailwindcss": "^3.4.4", - "ts-jest": "^29.1.5", - "typescript": "^5.5.2", - "vite": "^4.5.3" + "postcss": "^8.4.39", + "tailwindcss": "^3.4.6", + "ts-jest": "^29.2.3", + "typescript": "^5.5.3", + "vite": "^5.3.4" } }, "node_modules/@alloc/quick-lru": { @@ -72,6 +72,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -95,30 +96,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", + "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", + "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", + "@babel/generator": "^7.24.9", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-module-transforms": "^7.24.9", + "@babel/helpers": "^7.24.8", + "@babel/parser": "^7.24.8", "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -137,18 +140,19 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.24.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", + "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.24.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -157,27 +161,16 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", + "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -189,6 +182,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -245,9 +239,10 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", + "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.24.7", @@ -264,9 +259,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -276,6 +272,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.24.7", @@ -298,9 +295,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -316,22 +313,24 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", + "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -353,9 +352,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", "license": "MIT", "bin": { "parser": "bin/babel-parser.js" @@ -433,6 +432,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" @@ -587,9 +587,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -613,19 +613,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", + "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", + "@babel/generator": "^7.24.8", "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-function-name": "^7.24.7", "@babel/helper-hoist-variables": "^7.24.7", "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/parser": "^7.24.8", + "@babel/types": "^7.24.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -634,12 +634,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", + "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -655,9 +655,9 @@ "license": "MIT" }, "node_modules/@codemirror/autocomplete": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.3.tgz", - "integrity": "sha512-Vl/tIeRVVUCRDuOG48lttBasNQu8usGgXQawBXI7WJAiUDSFOfzflmEsZFZo48mAvAaa4FZ/4/yLLxFtdJaKYA==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.17.0.tgz", + "integrity": "sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -739,9 +739,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.28.2", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.2.tgz", - "integrity": "sha512-A3DmyVfjgPsGIjiJqM/zvODUAPQdQl3ci0ghehYNnbt5x+o76xq+dL5+mMBuysDXnI3kapgOkoeJ0sbtL/3qPw==", + "version": "6.28.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.6.tgz", + "integrity": "sha512-bhwB1AZ6zU4M3dNKm8Aa2BXwj5mWDqE9IWpqxYKJoLCnx+AcwcMuLO01tLWgc1mx4vT1IVYVqx86YoqUsATrqQ==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.4.0", @@ -750,16 +750,16 @@ } }, "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", + "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.2.0", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", @@ -768,12 +768,6 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" - }, "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -802,60 +796,58 @@ } }, "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.12.0.tgz", + "integrity": "sha512-VFo/F1PthkxHwWDCcXkidyXw70eAkdiNiCzthMI2rRQjFiTvmXt8UDlv/VE1DTsd4CIEY2wQf5AnL2QiPgphlw==", "license": "MIT", "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.3.0", + "@emotion/utils": "^1.3.0", + "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, - "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", "license": "MIT" }, - "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", "license": "MIT" }, - "node_modules/@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emotion/memoize": "0.7.4" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", - "license": "MIT", - "optional": true - }, "node_modules/@emotion/react": { - "version": "11.11.4", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", - "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.12.0.tgz", + "integrity": "sha512-kTktYMpG8mHjLi8u6XOTMfDmQvUve/un2ZVj4khcU2KTn17ElMV8BK6QFzT8V/v2QW8013rf07Yc0ayQL3tp3w==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/cache": "^11.12.0", + "@emotion/serialize": "^1.2.0", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "@emotion/utils": "^1.3.0", + "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { @@ -868,40 +860,28 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", - "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.2.0.tgz", + "integrity": "sha512-X5UWpZAhGGp5LOn7OAI9k9JjRtz7nSFhZypatADcuEd/0bECZ0DzVjPdL8hljTrAku8+TjFvWIYHMOCO/0v/Ng==", "license": "MIT", "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.9.0", + "@emotion/utils": "^1.3.0", "csstype": "^3.0.2" } }, - "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" - }, "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", - "license": "MIT" - }, - "node_modules/@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.3.0.tgz", + "integrity": "sha512-vOPwbKw8fj/oSEa7CWqiKCvLZ1AeLIAApmboGP34xUyUjXalFyf+tMtgMDqP7VMevLPhUa+YWJS46cQUA+tr9A==", "license": "MIT" }, "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.9.0.tgz", + "integrity": "sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ==", "license": "MIT" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { @@ -914,21 +894,38 @@ } }, "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.3.0.tgz", + "integrity": "sha512-+M7u4EaX5t4bCunKTltAdGis3NFHQniikLVEQ+rPQccsX/xV4v5Etwg12paioZ9DsO+CTvimtmnjZbW85kbF8Q==", "license": "MIT" }, "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, + "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==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -943,9 +940,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "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==", "cpu": [ "arm64" ], @@ -960,9 +957,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -977,9 +974,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -994,9 +991,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -1011,9 +1008,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -1028,9 +1025,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "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==", "cpu": [ "x64" ], @@ -1045,9 +1042,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -1062,9 +1059,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -1079,9 +1076,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -1096,9 +1093,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "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==", "cpu": [ "loong64" ], @@ -1113,9 +1110,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -1130,9 +1127,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "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==", "cpu": [ "ppc64" ], @@ -1147,9 +1144,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -1164,9 +1161,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -1181,9 +1178,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -1198,9 +1195,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -1215,9 +1212,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -1232,9 +1229,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "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==", "cpu": [ "x64" ], @@ -1249,9 +1246,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -1266,9 +1263,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "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==", "cpu": [ "ia32" ], @@ -1283,9 +1280,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -1316,9 +1313,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, "license": "MIT", "engines": { @@ -1413,28 +1410,28 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", - "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", + "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.0" + "@floating-ui/utils": "^0.2.4" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", - "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", + "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.4" } }, "node_modules/@floating-ui/utils": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", - "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", + "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==", "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { @@ -2518,9 +2515,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -2625,25 +2622,49 @@ "license": "Apache-2.0" }, "node_modules/@microsoft/tsdoc": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", "dev": true, "license": "MIT" }, "node_modules/@microsoft/tsdoc-config": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", - "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz", + "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==", "dev": true, "license": "MIT", "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "ajv": "~6.12.6", + "@microsoft/tsdoc": "0.15.0", + "ajv": "~8.12.0", "jju": "~1.4.0", - "resolve": "~1.19.0" + "resolve": "~1.22.2" } }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2835,6 +2856,36 @@ } } }, + "node_modules/@reactflow/background": { + "version": "11.3.14", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz", + "integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.14", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz", + "integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/@reactflow/core": { "version": "11.11.4", "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz", @@ -2856,15 +2907,290 @@ "react-dom": ">=17" } }, + "node_modules/@reactflow/minimap": { + "version": "11.7.14", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz", + "integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.14", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz", + "integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz", + "integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/@remix-run/router": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", - "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz", + "integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==", "license": "MIT", "engines": { "node": ">=14.0.0" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", + "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", + "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", + "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", + "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", + "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", + "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", + "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", + "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", + "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", + "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", + "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", + "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", + "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", + "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", + "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", + "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2893,12 +3219,12 @@ } }, "node_modules/@tanstack/react-table": { - "version": "8.17.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.17.3.tgz", - "integrity": "sha512-5gwg5SvPD3lNAXPuJJz1fOCEZYk9/GeBFH3w/hCgnfyszOIzwkwgp5I7Q4MJtn0WECp84b5STQUDdmvGi8m3nA==", + "version": "8.19.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.19.3.tgz", + "integrity": "sha512-MtgPZc4y+cCRtU16y1vh1myuyZ2OdkWgMEBzyjYsoMWMicKZGZvcDnub3Zwb6XF2pj9iRMvm1SO1n57lS0vXLw==", "license": "MIT", "dependencies": { - "@tanstack/table-core": "8.17.3" + "@tanstack/table-core": "8.19.3" }, "engines": { "node": ">=12" @@ -2913,9 +3239,9 @@ } }, "node_modules/@tanstack/table-core": { - "version": "8.17.3", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.17.3.tgz", - "integrity": "sha512-mPBodDGVL+fl6d90wUREepHa/7lhsghg2A3vFpakEhrhtbIlgNAZiMr7ccTgak5qbHqF14Fwy+W1yFWQt+WmYQ==", + "version": "8.19.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.19.3.tgz", + "integrity": "sha512-IqREj9ADoml9zCAouIG/5kCGoyIxPFdqdyoxis9FisXFi5vT+iYfEfLosq4xkU/iDbMcEuAj+X8dWRLvKYDNoQ==", "license": "MIT", "engines": { "node": ">=12" @@ -3235,6 +3561,13 @@ "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", "license": "MIT" }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/geojson": { "version": "7946.0.14", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", @@ -3299,17 +3632,10 @@ "pretty-format": "^29.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { - "version": "20.14.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.7.tgz", - "integrity": "sha512-uTr2m2IbJJucF3KUxgnGOZvYbN0QgkGyWxG6973HCpMYFy2KfcgYuIwkJQMQkt1VbBMlvWRbpshFTLxnxCZjKQ==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "dev": true, "license": "MIT", "dependencies": { @@ -3372,13 +3698,6 @@ "@types/react": "*" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -3392,14 +3711,20 @@ "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", "license": "MIT" }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "license": "MIT" + }, "node_modules/@types/three": { - "version": "0.165.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.165.0.tgz", - "integrity": "sha512-AJK8JZAFNBF0kBXiAIl5pggYlzAGGA8geVYQXAcPCEDRbyA+oEjkpUBcJJrtNz6IiALwzGexFJGZG2yV3WsYBw==", + "version": "0.166.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.166.0.tgz", + "integrity": "sha512-FHMnpcdhdbdOOIYbfkTkUVpYMW53odxbTRwd0/xJpYnTzEsjnVnondGAvHZb4z06UW0vo6WPVuvH0/9qrxKx7g==", "license": "MIT", "peer": true, "dependencies": { - "@tweenjs/tween.js": "~23.1.1", + "@tweenjs/tween.js": "~23.1.2", "@types/stats.js": "*", "@types/webxr": "*", "fflate": "~0.8.2", @@ -3407,9 +3732,9 @@ } }, "node_modules/@types/webxr": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.17.tgz", - "integrity": "sha512-JYcclaQIlisHRXM9dMF7SeVvQ54kcYc7QK1eKCExCTLKWnZDxP4cp/rXH4Uoa1j5+5oQJ0Cc2sZC/PWiiG4q2g==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.19.tgz", + "integrity": "sha512-4hxA+NwohSgImdTSlPXEqDqqFktNgmTXQ05ff1uWam05tNGroCMp4G+4XVl6qWm1p7GQ/9oD41kAYsSssF6Mzw==", "license": "MIT" }, "node_modules/@types/yargs": { @@ -3430,34 +3755,32 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz", + "integrity": "sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/type-utils": "7.16.1", + "@typescript-eslint/utils": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3466,27 +3789,27 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.1.tgz", + "integrity": "sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/typescript-estree": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3495,17 +3818,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz", + "integrity": "sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3513,26 +3836,26 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.1.tgz", + "integrity": "sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.16.1", + "@typescript-eslint/utils": "7.16.1", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3541,13 +3864,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz", + "integrity": "sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==", "dev": true, "license": "MIT", "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3555,23 +3878,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz", + "integrity": "sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3584,43 +3907,40 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz", + "integrity": "sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/typescript-estree": "7.16.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz", + "integrity": "sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.16.1", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3628,9 +3948,9 @@ } }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.22.2.tgz", - "integrity": "sha512-zcHGkldLFN3cGoI5XdOGAkeW24yaAgrDEYoyPyWHODmPiNwybQQoZGnH3qUdzZwUaXtAcLWoAeOPzfNRW2yGww==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.0.tgz", + "integrity": "sha512-+k5nkRpUWGaHr1JWT8jcKsVewlXw5qBgSopm9LW8fZ6KnSNZBycz8kHxh0+WSvckmXEESGptkIsb7dlkmJT/hQ==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -3655,9 +3975,9 @@ } }, "node_modules/@uiw/codemirror-themes": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.22.2.tgz", - "integrity": "sha512-gsLHn6SUuV5iboBvGrM7YimzLFHQmsNlkGIYs3UaVUJTo/A/ZrKoSJNyPziShLRjBXA2UwKdBTIU6VhHyyaChw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.23.0.tgz", + "integrity": "sha512-9fiji9xooZyBQozR1i6iTr56YP7j/Dr/VgsNWbqf5Szv+g+4WM1iZuiDGwNXmFMWX8gbkDzp6ASE21VCPSofWw==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -3674,16 +3994,16 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.22.2.tgz", - "integrity": "sha512-okCSl+WJG63gRx8Fdz7v0C6RakBQnbb3pHhuzIgDB+fwhipgFodSnu2n9oOsQesJ5YQ7mSOcKMgX0JEsu4nnfQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.0.tgz", + "integrity": "sha512-MnqTXfgeLA3fsUUQjqjJgemEuNyoGALgsExVm0NQAllAAi1wfj+IoKFeK+h3XXMlTFRCFYOUh4AHDv0YXJLsOg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.22.2", + "@uiw/codemirror-extensions-basic-setup": "4.23.0", "codemirror": "^6.0.0" }, "funding": { @@ -3758,9 +4078,9 @@ "optional": true }, "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "license": "MIT", "bin": { @@ -3915,6 +4235,13 @@ "node": ">=8" } }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4143,22 +4470,6 @@ "npm": ">=6" } }, - "node_modules/babel-plugin-styled-components": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", - "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "lodash": "^4.17.21", - "picomatch": "^2.3.1" - }, - "peerDependencies": { - "styled-components": ">= 2" - } - }, "node_modules/babel-preset-current-node-syntax": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", @@ -4253,9 +4564,10 @@ } }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", + "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -4272,10 +4584,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", + "caniuse-lite": "^1.0.30001640", + "electron-to-chromium": "^1.4.820", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -4362,9 +4674,10 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001636", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", - "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", + "version": "1.0.30001642", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", + "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -4626,6 +4939,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, "license": "MIT" }, "node_modules/cosmiconfig": { @@ -4820,9 +5134,9 @@ "license": "MIT" }, "node_modules/ctrl-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ctrl-keys/-/ctrl-keys-1.0.2.tgz", - "integrity": "sha512-MBVjzR+RlUqbgmDRbnrGsXdFo0xB5/OxO4o54tyhBvPtbZ/f5hPl/De4i5yEGPyKPKyyEbLCxR5qmY+NEbXnCA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ctrl-keys/-/ctrl-keys-1.0.3.tgz", + "integrity": "sha512-Kcb05/xUNra57fxpsLOflECWYbjQEQ9ZuQEthB3cgESN5zMLJ364twA9h2kqz8n06RnTY/+rKWM3UbkOWKeEJg==", "license": "MIT", "engines": { "node": ">=10" @@ -5134,9 +5448,9 @@ } }, "node_modules/detect-gpu": { - "version": "5.0.38", - "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.38.tgz", - "integrity": "sha512-36QeGHSXYcJ/RfrnPEScR8GDprbXFG4ZhXsfVNVHztZr38+fRxgHnJl3CjYXXjbeRUhu3ZZBJh6Lg0A9v0Qd8A==", + "version": "5.0.39", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.39.tgz", + "integrity": "sha512-qs+7gnNNxsH4RN1IPpQieU2XNO+RhgemuaRhcawiUug6oXb0Glup90H1YGSjslPO30Sw0E4yfjRoGtSEURwVPQ==", "license": "MIT", "dependencies": { "webgl-constants": "^1.1.1" @@ -5235,10 +5549,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.4.808", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.808.tgz", - "integrity": "sha512-0ItWyhPYnww2VOuCGF4s1LTfbrdAV2ajy/TN+ZTuhR23AHI6rWHCrBXJ/uxoXOvRRqw8qjYVrG81HFI7x/2wdQ==", + "version": "1.4.830", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.830.tgz", + "integrity": "sha512-TrPKKH20HeN0J1LHzsYLs2qwXrp8TF4nHdu4sq61ozGbzMpWhI7iIOPYPPkxeq1azMT9PZ8enPFcftbs/Npcjg==", + "dev": true, "license": "ISC" }, "node_modules/ellipsize": { @@ -5277,9 +5608,9 @@ } }, "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5290,34 +5621,36 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" + "@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" } }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5402,9 +5735,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.7.tgz", - "integrity": "sha512-yrj+KInFmwuQS2UQcg1SF83ha1tuHC1jMQbRNyuWtlEzzKRDgAl7L4Yp4NlDUZTZNlWvHEzOtJhMi40R7JxcSw==", + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.8.tgz", + "integrity": "sha512-MIKAclwaDFIiYtVBLzDdm16E+Ty4GwhB6wZlCAG1R3Ur+F9Qbo6PRxpA5DK7XtDgm+WlCoAY2WxAwqhmIDHg6Q==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5412,9 +5745,9 @@ } }, "node_modules/eslint-plugin-simple-import-sort": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz", - "integrity": "sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5422,14 +5755,14 @@ } }, "node_modules/eslint-plugin-tsdoc": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", - "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.3.0.tgz", + "integrity": "sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==", "dev": true, "license": "MIT", "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "0.16.2" + "@microsoft/tsdoc": "0.15.0", + "@microsoft/tsdoc-config": "0.17.0" } }, "node_modules/eslint-scope": { @@ -5637,9 +5970,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5831,6 +6164,29 @@ "node": "^10.12.0 || >=12.0.0" } }, + "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/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", @@ -5968,21 +6324,22 @@ } }, "node_modules/framer-motion": { - "version": "10.18.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", - "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", + "version": "11.3.8", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.3.8.tgz", + "integrity": "sha512-1D+RDTsIp4Rz2dq/oToqSEc9idEQwgBRQyBq4rGpFba+0Z+GCbj9z1s0+ikFbanWe3YJ0SqkNlDe08GcpFGj5A==", "license": "MIT", "dependencies": { "tslib": "^2.4.0" }, - "optionalDependencies": { - "@emotion/is-prop-valid": "^0.8.2" - }, "peerDependencies": { + "@emotion/is-prop-valid": "*", "react": "^18.0.0", "react-dom": "^18.0.0" }, "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, "react": { "optional": true }, @@ -6081,6 +6438,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -6590,9 +6948,9 @@ } }, "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -6697,9 +7055,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "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": { @@ -6802,17 +7160,14 @@ } }, "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -6820,6 +7175,125 @@ "@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/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -7798,24 +8272,6 @@ "node": ">=8" } }, - "node_modules/jest-resolve/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/jest-resolve/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8580,6 +9036,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -8664,12 +9121,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -8700,19 +9151,20 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/maath": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.7.tgz", - "integrity": "sha512-zQ2xd7dNOIVTjAS+hj22fyj1EFYmOJX6tzKjZ92r6WDoq8hyFxjuGA2q950tmR4iC/EKXoMQdSipkaJVuUHDTg==", + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz", + "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", "license": "MIT", "peerDependencies": { - "@types/three": ">=0.144.0", - "three": ">=0.144.0" + "@types/three": ">=0.134.0", + "three": ">=0.134.0" } }, "node_modules/make-cancellable-promise": { @@ -8880,9 +9332,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -8990,7 +9442,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -9041,9 +9492,10 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz", + "integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==", + "dev": true, "license": "MIT" }, "node_modules/nopt": { @@ -9319,14 +9771,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC", - "engines": { - "node": "14 || >=16.14" - } + "license": "ISC" }, "node_modules/path-type": { "version": "4.0.0", @@ -9338,9 +9787,9 @@ } }, "node_modules/path2d": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.0.tgz", - "integrity": "sha512-KdPAykQX6kmLSOO6Jpu2KNcCED7CKjmaBNGGNuctOsG0hgYO1OdYQaan6cYXJiG0WmXOwZZPILPBimu5QAIw3A==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.1.tgz", + "integrity": "sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==", "license": "MIT", "optional": true, "engines": { @@ -9348,9 +9797,9 @@ } }, "node_modules/pdfjs-dist": { - "version": "4.3.136", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.3.136.tgz", - "integrity": "sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==", + "version": "4.4.168", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.4.168.tgz", + "integrity": "sha512-MbkAjpwka/dMHaCfQ75RY1FXX3IewBVu6NGZOcxerRFlaBiIkZmUoR0jotX5VUzYZEXAGzSFtknWs5xRKliXPA==", "license": "Apache-2.0", "engines": { "node": ">=18" @@ -9370,6 +9819,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -9468,9 +9918,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "dev": true, "funding": [ { @@ -9489,7 +9939,7 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -9617,9 +10067,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", + "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", "dev": true, "license": "MIT", "dependencies": { @@ -9815,9 +10265,9 @@ } }, "node_modules/react-icons": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", - "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", "license": "MIT", "peerDependencies": { "react": "*" @@ -9857,14 +10307,16 @@ "license": "MIT" }, "node_modules/react-loader-spinner": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.4.5.tgz", - "integrity": "sha512-32f+sb/v2tnNfyvnCCOS4fpyVHsGXjSyNo6QLniHcaj1XjKLxx14L2z0h6szRugOL8IEJ+53GPwNAdbkDqmy4g==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.1.6.tgz", + "integrity": "sha512-x5h1Jcit7Qn03MuKlrWcMG9o12cp9SNDVHVJTNRi9TgtGPKcjKiXkou4NRfLAtXaFB3+Z8yZsVzONmPzhv2ErA==", "license": "MIT", "dependencies": { "react-is": "^18.2.0", - "styled-components": "^5.3.5", - "styled-tools": "^1.7.2" + "styled-components": "^6.1.2" + }, + "engines": { + "node": ">= 12" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0", @@ -9882,9 +10334,9 @@ } }, "node_modules/react-pdf": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-9.0.0.tgz", - "integrity": "sha512-J+pza8R2p9oNEOJOHIQJI4o5rFK7ji7bBl2IvsHvz1OOyphvuzVDo5tOJwWAFAbxYauCH3Kt8jOvcMJUOpxYZQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-9.1.0.tgz", + "integrity": "sha512-KhPDQE3QshkLdS3b48S5Bldv0N5flob6qwvsiADWdZOS5TMDaIrkRtEs+Dyl6ubRf2jTf9jWmFb6RjWu46lSSg==", "license": "MIT", "dependencies": { "clsx": "^2.0.0", @@ -9892,7 +10344,7 @@ "make-cancellable-promise": "^1.3.1", "make-event-props": "^1.6.0", "merge-refs": "^1.3.0", - "pdfjs-dist": "4.3.136", + "pdfjs-dist": "4.4.168", "tiny-invariant": "^1.0.0", "warning": "^4.0.0" }, @@ -9946,12 +10398,12 @@ } }, "node_modules/react-router": { - "version": "6.23.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz", - "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==", + "version": "6.25.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz", + "integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.16.1" + "@remix-run/router": "1.18.0" }, "engines": { "node": ">=14.0.0" @@ -9961,13 +10413,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.23.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz", - "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==", + "version": "6.25.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz", + "integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.16.1", - "react-router": "6.23.1" + "@remix-run/router": "1.18.0", + "react-router": "6.25.1" }, "engines": { "node": ">=14.0.0" @@ -10012,31 +10464,22 @@ } }, "node_modules/react-toastify": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", - "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz", + "integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==", "license": "MIT", "dependencies": { - "clsx": "^1.1.1" + "clsx": "^2.1.0" }, "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/react-toastify/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "license": "MIT", - "engines": { - "node": ">=6" + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/react-tooltip": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.27.0.tgz", - "integrity": "sha512-JXROcdfCEbCqkAkh8LyTSP3guQ0dG53iY2E2o4fw3D8clKzziMpE6QG6CclDaHELEKTzpMSeAOsdtg0ahoQosw==", + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.27.1.tgz", + "integrity": "sha512-a+micPXcMOMt11CYlwJD4XShcqGziasHco4NPe1OFw298WBTILMyzUgNC1LAFViAe791JdHNVSJIpzhZm2MvDA==", "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.6.1", @@ -10086,6 +10529,24 @@ "react-dom": ">=16.13" } }, + "node_modules/reactflow": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz", + "integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==", + "license": "MIT", + "dependencies": { + "@reactflow/background": "11.3.14", + "@reactflow/controls": "11.2.14", + "@reactflow/core": "11.11.4", + "@reactflow/minimap": "11.7.14", + "@reactflow/node-resizer": "2.2.14", + "@reactflow/node-toolbar": "1.3.14" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -10125,9 +10586,9 @@ } }, "node_modules/reagraph": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/reagraph/-/reagraph-4.19.1.tgz", - "integrity": "sha512-BMSfZ2CoLSWsxe+vowHbCZSqeJJlH2UcbQA6hM1JHI0PmZ/3UHIXCQUHXrsROtYIIH5AnFEj9pUAZh2FGnfGhw==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/reagraph/-/reagraph-4.19.2.tgz", + "integrity": "sha512-SfEaXGRR/kWwG6Hq8Q6WropdhMAFgszWG174/j4rsZQNMYTEiwwK+47iQLW9qxwVO1YJAloktfihN7QsWZmLaw==", "license": "Apache-2.0", "dependencies": { "@react-spring/three": "9.6.1", @@ -10148,7 +10609,7 @@ "graphology-shortest-path": "^2.0.2", "hold-event": "^0.2.0", "react-use-gesture": "^9.1.3", - "reakeys": "^2.0.1", + "reakeys": "^2.0.3", "three": "^0.154.0", "three-stdlib": "^2.23.13", "zustand": "4.3.9" @@ -10183,12 +10644,12 @@ } }, "node_modules/reakeys": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/reakeys/-/reakeys-2.0.2.tgz", - "integrity": "sha512-xY6OvVxuSC3iH55wvfPDyZ5xxMXIzEp6tNdipNXkaP6eznC9NdH+S3OqX3gktY731oIX31zH7u0QBV4lRXo1iQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/reakeys/-/reakeys-2.0.3.tgz", + "integrity": "sha512-5qeGH9xtvFITi+9AyPeTmPhzjDTEBRZICxAg6RJFuEgWFKMHqr6mnMIaL9fgOKJMBzLWCBorpUhyiB824f0EyA==", "license": "Apache-2.0", "dependencies": { - "ctrl-keys": "^1.0.2" + "ctrl-keys": "^1.0.3" }, "peerDependencies": { "react": ">=16", @@ -10221,13 +10682,17 @@ } }, "node_modules/resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "license": "MIT", "dependencies": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10304,19 +10769,38 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", + "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", "dev": true, "license": "MIT", + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.18.1", + "@rollup/rollup-android-arm64": "4.18.1", + "@rollup/rollup-darwin-arm64": "4.18.1", + "@rollup/rollup-darwin-x64": "4.18.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", + "@rollup/rollup-linux-arm-musleabihf": "4.18.1", + "@rollup/rollup-linux-arm64-gnu": "4.18.1", + "@rollup/rollup-linux-arm64-musl": "4.18.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", + "@rollup/rollup-linux-riscv64-gnu": "4.18.1", + "@rollup/rollup-linux-s390x-gnu": "4.18.1", + "@rollup/rollup-linux-x64-gnu": "4.18.1", + "@rollup/rollup-linux-x64-musl": "4.18.1", + "@rollup/rollup-win32-arm64-msvc": "4.18.1", + "@rollup/rollup-win32-ia32-msvc": "4.18.1", + "@rollup/rollup-win32-x64-msvc": "4.18.1", "fsevents": "~2.3.2" } }, @@ -10375,9 +10859,9 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "devOptional": true, "license": "ISC", "bin": { @@ -10492,7 +10976,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -10689,24 +11172,23 @@ "license": "MIT" }, "node_modules/styled-components": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", - "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz", + "integrity": "sha512-n/O4PzRPhbYI0k1vKKayfti3C/IGcPf+DqcrOB7O/ab9x4u/zjqraneT5N45+sIe87cxrCApXM8Bna7NYxwoTA==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", - "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" }, "engines": { - "node": ">=10" + "node": ">= 16" }, "funding": { "type": "opencollective", @@ -10714,37 +11196,55 @@ }, "peerDependencies": { "react": ">= 16.8.0", - "react-dom": ">= 16.8.0", - "react-is": ">= 16.8.0" + "react-dom": ">= 16.8.0" } }, - "node_modules/styled-components/node_modules/@emotion/is-prop-valid": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", - "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/styled-components/node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" - }, "node_modules/styled-components/node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", "license": "MIT" }, - "node_modules/styled-tools": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/styled-tools/-/styled-tools-1.7.2.tgz", - "integrity": "sha512-IjLxzM20RMwAsx8M1QoRlCG/Kmq8lKzCGyospjtSXt/BTIIcvgTonaxQAsKnBrsZNwhpHzO9ADx5te0h76ILVg==", + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", "license": "MIT" }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -10775,9 +11275,9 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { @@ -10791,25 +11291,6 @@ "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -10840,7 +11321,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10859,9 +11339,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz", - "integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==", + "version": "3.4.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.6.tgz", + "integrity": "sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==", "dev": true, "license": "MIT", "dependencies": { @@ -10896,24 +11376,6 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -11015,18 +11477,18 @@ "license": "MIT" }, "node_modules/three-mesh-bvh": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.5.tgz", - "integrity": "sha512-WDd77RklE52pZSKZx8sDXzrd2JCF/gL/hugFvsIBylpMRlJxxwesKn2rW7TcQZ809NocDVkQx1UJo9pJtVAPYg==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.6.tgz", + "integrity": "sha512-rCjsnxEqR9r1/C/lCqzGLS67NDty/S/eT6rAJfDvsanrIctTWdNoR4ZOGWewCB13h1QkVo2BpmC0wakj1+0m8A==", "license": "MIT", "peerDependencies": { "three": ">= 0.151.0" } }, "node_modules/three-stdlib": { - "version": "2.30.3", - "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.30.3.tgz", - "integrity": "sha512-rYr8PqMljMza+Ct8kQk90Y7y+YcWoPu1thfYv5YGCp0hytNRbxSQWXY4GpdTGymCj3bDggEBpxso53C3pPwhIw==", + "version": "2.30.4", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.30.4.tgz", + "integrity": "sha512-E7sN8UkaorSq2uRZU14AE7wXkdCBa2oFwPkPt92zaecuzrgd98BXkTt+2tFQVF1tPJRvfs7aMZV5dSOq4/vNVg==", "license": "MIT", "dependencies": { "@types/draco3d": "^1.4.0", @@ -11139,13 +11601,14 @@ "license": "Apache-2.0" }, "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.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.3.tgz", + "integrity": "sha512-yCcfVdiBFngVz9/keHin9EnsrQtQtEu3nRykNy9RVp+FiPFFbPJ3Sg6Qg4+TkmH0vMP5qsTKgXSsk80HRwvdgQ==", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "0.x", + "ejs": "^3.1.10", "fast-json-stable-stringify": "2.x", "jest-util": "^29.0.0", "json5": "^2.2.3", @@ -11238,9 +11701,9 @@ } }, "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -11259,9 +11722,10 @@ "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -11363,9 +11827,9 @@ } }, "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", "dependencies": { @@ -11378,30 +11842,30 @@ } }, "node_modules/vite": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", - "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz", + "integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.21.3", + "postcss": "^8.4.39", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", @@ -11666,6 +12130,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, "license": "ISC" }, "node_modules/yaml": { @@ -11720,9 +12185,9 @@ } }, "node_modules/zustand": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", - "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.4.tgz", + "integrity": "sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==", "license": "MIT", "dependencies": { "use-sync-external-store": "1.2.0" diff --git a/rsconcept/frontend/package.json b/rsconcept/frontend/package.json index 5f4033ce..7433fa66 100644 --- a/rsconcept/frontend/package.json +++ b/rsconcept/frontend/package.json @@ -13,50 +13,50 @@ }, "dependencies": { "@lezer/lr": "^1.4.1", - "@reactflow/core": "^11.11.4", - "@tanstack/react-table": "^8.17.3", - "@uiw/codemirror-themes": "^4.22.2", - "@uiw/react-codemirror": "^4.22.2", + "@tanstack/react-table": "^8.19.3", + "@uiw/codemirror-themes": "^4.23.0", + "@uiw/react-codemirror": "^4.23.0", "axios": "^1.7.2", "clsx": "^2.1.1", - "framer-motion": "^10.18.0", + "framer-motion": "^11.3.8", "js-file-download": "^0.4.12", "react": "^18.3.1", "react-dom": "^18.3.1", "react-error-boundary": "^4.0.13", - "react-icons": "^4.12.0", + "react-icons": "^5.2.1", "react-intl": "^6.6.8", - "react-loader-spinner": "^5.4.5", - "react-pdf": "^9.0.0", - "react-router-dom": "^6.23.1", + "react-loader-spinner": "^6.1.6", + "react-pdf": "^9.1.0", + "react-router-dom": "^6.25.1", "react-select": "^5.8.0", "react-tabs": "^6.0.2", - "react-toastify": "^9.1.3", - "react-tooltip": "^5.27.0", - "reagraph": "^4.19.1", + "react-toastify": "^10.0.5", + "react-tooltip": "^5.27.1", + "reactflow": "^11.11.4", + "reagraph": "^4.19.2", "use-debounce": "^10.0.1" }, "devDependencies": { "@lezer/generator": "^1.7.1", "@types/jest": "^29.5.12", - "@types/node": "^20.14.7", + "@types/node": "^20.14.11", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.16.1", + "@typescript-eslint/parser": "^7.16.1", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.19", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.7", - "eslint-plugin-simple-import-sort": "^10.0.0", - "eslint-plugin-tsdoc": "^0.2.17", + "eslint-plugin-react-refresh": "^0.4.8", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-tsdoc": "^0.3.0", "jest": "^29.7.0", - "postcss": "^8.4.38", - "tailwindcss": "^3.4.4", - "ts-jest": "^29.1.5", - "typescript": "^5.5.2", - "vite": "^4.5.3" + "postcss": "^8.4.39", + "tailwindcss": "^3.4.6", + "ts-jest": "^29.2.3", + "typescript": "^5.5.3", + "vite": "^5.3.4" }, "jest": { "preset": "ts-jest", diff --git a/rsconcept/frontend/public/robots.txt b/rsconcept/frontend/public/robots.txt index a7022980..65568494 100644 --- a/rsconcept/frontend/public/robots.txt +++ b/rsconcept/frontend/public/robots.txt @@ -1,2 +1,5 @@ User-agent: * -Disallow: /library \ No newline at end of file +Disallow: /library +Disallow: /restore-password +Disallow: /signup +Disallow: /profile \ No newline at end of file diff --git a/rsconcept/frontend/src/app/ApplicationLayout.tsx b/rsconcept/frontend/src/app/ApplicationLayout.tsx index 84b95b75..f592d4a2 100644 --- a/rsconcept/frontend/src/app/ApplicationLayout.tsx +++ b/rsconcept/frontend/src/app/ApplicationLayout.tsx @@ -3,8 +3,8 @@ import { Outlet } from 'react-router-dom'; import ConceptToaster from '@/app/ConceptToaster'; import Footer from '@/app/Footer'; import Navigation from '@/app/Navigation'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { NavigationState } from '@/context/NavigationContext'; -import { useConceptOptions } from '@/context/OptionsContext'; import { globals } from '@/utils/constants'; function ApplicationLayout() { diff --git a/rsconcept/frontend/src/app/ConceptToaster.tsx b/rsconcept/frontend/src/app/ConceptToaster.tsx index 09e361f2..262759e8 100644 --- a/rsconcept/frontend/src/app/ConceptToaster.tsx +++ b/rsconcept/frontend/src/app/ConceptToaster.tsx @@ -1,6 +1,6 @@ import { ToastContainer, type ToastContainerProps } from 'react-toastify'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; interface ToasterThemedProps extends Omit {} diff --git a/rsconcept/frontend/src/app/Footer.tsx b/rsconcept/frontend/src/app/Footer.tsx index bae26d81..c4f98a8f 100644 --- a/rsconcept/frontend/src/app/Footer.tsx +++ b/rsconcept/frontend/src/app/Footer.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { external_urls } from '@/utils/constants'; import TextURL from '../components/ui/TextURL'; diff --git a/rsconcept/frontend/src/app/GlobalProviders.tsx b/rsconcept/frontend/src/app/GlobalProviders.tsx index d62c99d5..db0369e9 100644 --- a/rsconcept/frontend/src/app/GlobalProviders.tsx +++ b/rsconcept/frontend/src/app/GlobalProviders.tsx @@ -5,8 +5,8 @@ import { IntlProvider } from 'react-intl'; import { pdfjs } from 'react-pdf'; import { AuthState } from '@/context/AuthContext'; +import { OptionsState } from '@/context/ConceptOptionsContext'; import { LibraryState } from '@/context/LibraryContext'; -import { OptionsState } from '@/context/OptionsContext'; import { UsersState } from '@/context/UsersContext'; import ErrorFallback from './ErrorFallback'; diff --git a/rsconcept/frontend/src/app/Navigation/Logo.tsx b/rsconcept/frontend/src/app/Navigation/Logo.tsx index a4d16662..e4097fbb 100644 --- a/rsconcept/frontend/src/app/Navigation/Logo.tsx +++ b/rsconcept/frontend/src/app/Navigation/Logo.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import useWindowSize from '@/hooks/useWindowSize'; function Logo() { diff --git a/rsconcept/frontend/src/app/Navigation/Navigation.tsx b/rsconcept/frontend/src/app/Navigation/Navigation.tsx index 207dccae..c694a2ba 100644 --- a/rsconcept/frontend/src/app/Navigation/Navigation.tsx +++ b/rsconcept/frontend/src/app/Navigation/Navigation.tsx @@ -3,14 +3,14 @@ import { motion } from 'framer-motion'; import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons'; import { CProps } from '@/components/props'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useConceptNavigation } from '@/context/NavigationContext'; -import { useConceptOptions } from '@/context/OptionsContext'; import { animateNavigation } from '@/styling/animations'; import { urls } from '../urls'; import Logo from './Logo'; import NavigationButton from './NavigationButton'; -import ToggleNavigationButton from './ToggleNavigationButton'; +import ToggleNavigation from './ToggleNavigation'; import UserMenu from './UserMenu'; function Navigation() { @@ -33,7 +33,7 @@ function Navigation() { 'select-none' )} > - +
- } - onClick={navigateCreateNew} - /> - } - onClick={navigateLibrary} - /> - } onClick={navigateHelp} /> + } onClick={navigateCreateNew} /> + } onClick={navigateLibrary} /> + } onClick={navigateHelp} />
diff --git a/rsconcept/frontend/src/app/Navigation/ToggleNavigationButton.tsx b/rsconcept/frontend/src/app/Navigation/ToggleNavigation.tsx similarity index 88% rename from rsconcept/frontend/src/app/Navigation/ToggleNavigationButton.tsx rename to rsconcept/frontend/src/app/Navigation/ToggleNavigation.tsx index ce564c40..ed4e4307 100644 --- a/rsconcept/frontend/src/app/Navigation/ToggleNavigationButton.tsx +++ b/rsconcept/frontend/src/app/Navigation/ToggleNavigation.tsx @@ -2,11 +2,11 @@ import clsx from 'clsx'; import { motion } from 'framer-motion'; import { IconPin, IconUnpin } from '@/components/Icons'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { animateNavigationToggle } from '@/styling/animations'; import { globals } from '@/utils/constants'; -function ToggleNavigationButton() { +function ToggleNavigation() { const { noNavigationAnimation, toggleNoNavigation } = useConceptOptions(); return ( { export interface FrontPush extends IFrontRequest { data: DataType; } - export interface FrontPull extends IFrontRequest { onSuccess: DataCallback; } @@ -228,10 +234,27 @@ export function postCloneLibraryItem(target: string, request: FrontExchange) { - AxiosGet({ - endpoint: `/api/synthesis/${target}`, - request: request + request.setLoading!(false); + request.onSuccess({ + id: Number(target), + comment: '123', + alias: 'oss1', + access_policy: AccessPolicy.PUBLIC, + editors: [], + owner: 1, + item_type: LibraryItemType.OSS, + location: '/U', + read_only: false, + subscribers: [], + time_create: '0', + time_update: '0', + title: 'TestOss', + visible: false }); + // AxiosGet({ + // endpoint: `/api/oss/${target}`, // TODO: endpoint to access OSS + // request: request + // }); } export function getRSFormDetails(target: string, version: string, request: FrontPull) { @@ -415,35 +438,13 @@ export function patchUploadTRS(target: string, request: FrontExchange) { +export function patchInlineSynthesis(request: FrontExchange) { AxiosPatch({ endpoint: `/api/operations/inline-synthesis`, request: request }); } -export function runSingleSynthesis(request: FrontExchange) { - AxiosPost({ - endpoint: `/api/synthesis/run_single`, - request: request - }); -} - -export function postSynthesisGraph(request: FrontExchange) { - AxiosPost({ - endpoint: `/api/synthesis/save`, - request: request - }); -} - -export function postResolveText(schema: string, request: FrontExchange) { - AxiosPost({ - endpoint: `/api/rsforms/${schema}/resolve`, - request: request - }); -} - export function postInflectText(request: FrontExchange) { AxiosPost({ endpoint: `/api/cctext/inflect`, diff --git a/rsconcept/frontend/src/components/RSInput/RSInput.tsx b/rsconcept/frontend/src/components/RSInput/RSInput.tsx index 19ba811b..ebc673f0 100644 --- a/rsconcept/frontend/src/components/RSInput/RSInput.tsx +++ b/rsconcept/frontend/src/components/RSInput/RSInput.tsx @@ -9,7 +9,7 @@ import { EditorView } from 'codemirror'; import { forwardRef, useCallback, useMemo, useRef } from 'react'; import Label from '@/components/ui/Label'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { getFontClassName } from '@/models/miscellaneousAPI'; import { ConstituentaID, IRSForm } from '@/models/rsform'; import { generateAlias, getCstTypePrefix, guessCstType } from '@/models/rsformAPI'; diff --git a/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx b/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx index bdfe33fe..2c28313e 100644 --- a/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx +++ b/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx @@ -10,7 +10,7 @@ import { AnimatePresence } from 'framer-motion'; import { forwardRef, useCallback, useMemo, useRef, useState } from 'react'; import Label from '@/components/ui/Label'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import DlgEditReference from '@/dialogs/DlgEditReference'; import { ReferenceType } from '@/models/language'; import { ConstituentaID, IRSForm } from '@/models/rsform'; diff --git a/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx b/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx index 06427b59..207868f6 100644 --- a/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx +++ b/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx @@ -1,10 +1,11 @@ import clsx from 'clsx'; -import ConstituentaTooltip from '@/components/info/ConstituentaTooltip'; import { IConstituenta } from '@/models/rsform'; import { isMockCst } from '@/models/rsformAPI'; import { colorFgCstStatus, IColorTheme } from '@/styling/color'; +import TooltipConstituenta from './TooltipConstituenta'; + interface BadgeConstituentaProps { prefixID?: string; value: IConstituenta; @@ -28,7 +29,7 @@ function BadgeConstituenta({ value, prefixID, theme }: BadgeConstituentaProps) { }} > {value.alias} - + ); } diff --git a/rsconcept/frontend/src/components/info/BadgeGrammeme.tsx b/rsconcept/frontend/src/components/info/BadgeGrammeme.tsx index cf431f1a..55965d89 100644 --- a/rsconcept/frontend/src/components/info/BadgeGrammeme.tsx +++ b/rsconcept/frontend/src/components/info/BadgeGrammeme.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { GramData } from '@/models/language'; import { colorFgGrammeme } from '@/styling/color'; import { labelGrammeme } from '@/utils/labels'; diff --git a/rsconcept/frontend/src/components/info/BadgeHelp.tsx b/rsconcept/frontend/src/components/info/BadgeHelp.tsx index 77a68cde..e57be9da 100644 --- a/rsconcept/frontend/src/components/info/BadgeHelp.tsx +++ b/rsconcept/frontend/src/components/info/BadgeHelp.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import TextURL from '@/components/ui/TextURL'; import Tooltip, { PlacesType } from '@/components/ui/Tooltip'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { HelpTopic } from '@/models/miscellaneous'; import TopicPage from '../../pages/ManualsPage/TopicPage'; diff --git a/rsconcept/frontend/src/components/info/InfoCstClass.tsx b/rsconcept/frontend/src/components/info/InfoCstClass.tsx index 6580bc6e..02605505 100644 --- a/rsconcept/frontend/src/components/info/InfoCstClass.tsx +++ b/rsconcept/frontend/src/components/info/InfoCstClass.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { CstClass } from '@/models/rsform'; import { colorBgCstClass } from '@/styling/color'; import { prefixes } from '@/utils/constants'; diff --git a/rsconcept/frontend/src/components/info/InfoCstStatus.tsx b/rsconcept/frontend/src/components/info/InfoCstStatus.tsx index f3c8002c..ba95a5b9 100644 --- a/rsconcept/frontend/src/components/info/InfoCstStatus.tsx +++ b/rsconcept/frontend/src/components/info/InfoCstStatus.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { ExpressionStatus } from '@/models/rsform'; import { colorBgCstStatus } from '@/styling/color'; import { prefixes } from '@/utils/constants'; diff --git a/rsconcept/frontend/src/components/info/ConstituentaTooltip.tsx b/rsconcept/frontend/src/components/info/TooltipConstituenta.tsx similarity index 73% rename from rsconcept/frontend/src/components/info/ConstituentaTooltip.tsx rename to rsconcept/frontend/src/components/info/TooltipConstituenta.tsx index fc4f2601..42bab46d 100644 --- a/rsconcept/frontend/src/components/info/ConstituentaTooltip.tsx +++ b/rsconcept/frontend/src/components/info/TooltipConstituenta.tsx @@ -2,12 +2,12 @@ import InfoConstituenta from '@/components/info/InfoConstituenta'; import Tooltip from '@/components/ui/Tooltip'; import { IConstituenta } from '@/models/rsform'; -interface ConstituentaTooltipProps { +interface TooltipConstituentaProps { data: IConstituenta; anchor: string; } -function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) { +function TooltipConstituenta({ data, anchor }: TooltipConstituentaProps) { return ( event.stopPropagation()} /> @@ -15,4 +15,4 @@ function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) { ); } -export default ConstituentaTooltip; +export default TooltipConstituenta; diff --git a/rsconcept/frontend/src/components/select/PickConstituenta.tsx b/rsconcept/frontend/src/components/select/PickConstituenta.tsx index 5f656280..976c2f3b 100644 --- a/rsconcept/frontend/src/components/select/PickConstituenta.tsx +++ b/rsconcept/frontend/src/components/select/PickConstituenta.tsx @@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from 'react'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; import SearchBar from '@/components/ui/SearchBar'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { CstMatchMode } from '@/models/miscellaneous'; import { IConstituenta } from '@/models/rsform'; import { matchConstituenta } from '@/models/rsformAPI'; diff --git a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx index ab1e216f..ee7f0eb8 100644 --- a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx +++ b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx @@ -4,14 +4,14 @@ import clsx from 'clsx'; import { useLayoutEffect, useMemo, useState } from 'react'; import DataTable, { createColumnHelper, RowSelectionState } from '@/components/ui/DataTable'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform'; import { isBasicConcept } from '@/models/rsformAPI'; import { describeConstituenta } from '@/utils/labels'; import BadgeConstituenta from '../info/BadgeConstituenta'; import NoData from '../ui/NoData'; -import GraphSelectionToolbar from './GraphSelectionToolbar'; +import ToolbarGraphSelection from './ToolbarGraphSelection'; interface PickMultiConstituentaProps { id?: string; @@ -80,7 +80,7 @@ function PickMultiConstituenta({ id, schema, prefixID, rows, selected, setSelect Выбраны {selected.length} из {schema?.items.length ?? 0} {schema ? ( - isBasicConcept(cst.cst_type)).map(cst => cst.id)} setSelected={setSelected} diff --git a/rsconcept/frontend/src/components/select/PickSchema.tsx b/rsconcept/frontend/src/components/select/PickSchema.tsx index a7f8b897..24b2e521 100644 --- a/rsconcept/frontend/src/components/select/PickSchema.tsx +++ b/rsconcept/frontend/src/components/select/PickSchema.tsx @@ -3,8 +3,8 @@ import { useIntl } from 'react-intl'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; import SearchBar from '@/components/ui/SearchBar'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useLibrary } from '@/context/LibraryContext'; -import { useConceptOptions } from '@/context/OptionsContext'; import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library'; import { ILibraryFilter } from '@/models/miscellaneous'; diff --git a/rsconcept/frontend/src/components/select/PickSubstitutions.tsx b/rsconcept/frontend/src/components/select/PickSubstitutions.tsx index c66a73af..ada311e1 100644 --- a/rsconcept/frontend/src/components/select/PickSubstitutions.tsx +++ b/rsconcept/frontend/src/components/select/PickSubstitutions.tsx @@ -7,7 +7,7 @@ import SelectConstituenta from '@/components/select/SelectConstituenta'; import DataTable, { createColumnHelper } from '@/components/ui/DataTable'; import Label from '@/components/ui/Label'; import MiniButton from '@/components/ui/MiniButton'; -import { useConceptOptions } from '@/context/OptionsContext'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { IConstituenta, IRSForm, ISubstitution } from '@/models/rsform'; import { describeConstituenta } from '@/utils/labels'; diff --git a/rsconcept/frontend/src/components/select/SelectLocation.tsx b/rsconcept/frontend/src/components/select/SelectLocation.tsx index 43cbcd66..cded1871 100644 --- a/rsconcept/frontend/src/components/select/SelectLocation.tsx +++ b/rsconcept/frontend/src/components/select/SelectLocation.tsx @@ -1,40 +1,109 @@ 'use client'; -import { useCallback } from 'react'; +import clsx from 'clsx'; +import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; -import useDropdown from '@/hooks/useDropdown'; -import { FolderTree } from '@/models/FolderTree'; +import { FolderNode, FolderTree } from '@/models/FolderTree'; +import { labelFolderNode } from '@/utils/labels'; -import { IconFolderTree } from '../Icons'; +import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened } from '../Icons'; +import { CProps } from '../props'; import MiniButton from '../ui/MiniButton'; -interface SelectLocationProps { +interface SelectLocationProps extends CProps.Styling { value: string; - onChange: (newValue: string) => void; - folderTree: FolderTree; + prefix: string; + dense?: boolean; + onClick: (event: CProps.EventMouse, target: FolderNode) => void; } -function SelectLocation({ value, onChange, folderTree }: SelectLocationProps) { - const menu = useDropdown(); +function SelectLocation({ value, folderTree, dense, prefix, onClick, className, style }: SelectLocationProps) { + const activeNode = useMemo(() => folderTree.at(value), [folderTree, value]); - const handleChange = useCallback( - (newValue: string) => { - console.log(folderTree.roots.size); - console.log(value); - menu.hide(); - onChange(newValue); + const items = useMemo(() => folderTree.getTree(), [folderTree]); + const [folded, setFolded] = useState(items); + + useLayoutEffect(() => { + setFolded(items.filter(item => item !== activeNode && (!activeNode || !activeNode.hasPredecessor(item)))); + }, [items, activeNode]); + + const onFoldItem = useCallback( + (target: FolderNode, showChildren: boolean) => { + setFolded(prev => + items.filter(item => { + if (item === target) { + return !showChildren; + } + if (!showChildren && item.hasPredecessor(target)) { + return true; + } else { + return prev.includes(item); + } + }) + ); }, - [menu, onChange, value, folderTree] + [items] + ); + + const handleClickFold = useCallback( + (event: CProps.EventMouse, target: FolderNode, showChildren: boolean) => { + event.preventDefault(); + event.stopPropagation(); + onFoldItem(target, showChildren); + }, + [onFoldItem] ); return ( -
- } - onClick={() => handleChange('/U/test')} - /> +
+ {items.map((item, index) => + !item.parent || !folded.includes(item.parent) ? ( +
5 ? 5 : item.rank) * 0.5 + 0.5}rem` }} + onClick={event => onClick(event, item)} + > + {item.children.size > 0 ? ( + + ) : ( + + ) + ) : ( + + ) + } + onClick={event => handleClickFold(event, item, folded.includes(item))} + /> + ) : ( +
+ {item.filesInside ? ( + + ) : ( + + )} +
+ )} +
{labelFolderNode(item)}
+
+ ) : null + )}
); } diff --git a/rsconcept/frontend/src/components/select/SelectLocationContext.tsx b/rsconcept/frontend/src/components/select/SelectLocationContext.tsx new file mode 100644 index 00000000..91e260a8 --- /dev/null +++ b/rsconcept/frontend/src/components/select/SelectLocationContext.tsx @@ -0,0 +1,62 @@ +'use client'; + +import clsx from 'clsx'; +import { useCallback } from 'react'; + +import useDropdown from '@/hooks/useDropdown'; +import { FolderTree } from '@/models/FolderTree'; +import { prefixes } from '@/utils/constants'; + +import { IconFolderTree } from '../Icons'; +import { CProps } from '../props'; +import Dropdown from '../ui/Dropdown'; +import MiniButton from '../ui/MiniButton'; +import SelectLocation from './SelectLocation'; + +interface SelectLocationContextProps extends CProps.Styling { + value: string; + folderTree: FolderTree; + stretchTop?: boolean; + + onChange: (newValue: string) => void; +} + +function SelectLocationContext({ value, folderTree, onChange, className, style }: SelectLocationContextProps) { + const menu = useDropdown(); + + const handleClick = useCallback( + (event: CProps.EventMouse, newValue: string) => { + event.preventDefault(); + event.stopPropagation(); + menu.hide(); + onChange(newValue); + }, + [menu, onChange] + ); + + return ( +
+ } + onClick={() => menu.toggle()} + /> + + handleClick(event, target.getPath())} + /> + +
+ ); +} + +export default SelectLocationContext; diff --git a/rsconcept/frontend/src/dialogs/DlgEditReference/SelectWordForm.tsx b/rsconcept/frontend/src/components/select/SelectWordForm.tsx similarity index 93% rename from rsconcept/frontend/src/dialogs/DlgEditReference/SelectWordForm.tsx rename to rsconcept/frontend/src/components/select/SelectWordForm.tsx index 1db8c129..7f385da0 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditReference/SelectWordForm.tsx +++ b/rsconcept/frontend/src/components/select/SelectWordForm.tsx @@ -6,7 +6,7 @@ import { Grammeme } from '@/models/language'; import { prefixes } from '@/utils/constants'; import { DefaultWordForms, IGrammemeOption, SelectorGrammemes } from '@/utils/selectors'; -import WordformButton from './WordformButton'; +import WordformButton from '../../dialogs/DlgEditReference/WordformButton'; interface SelectWordFormProps { selected: IGrammemeOption[]; diff --git a/rsconcept/frontend/src/components/select/GraphSelectionToolbar.tsx b/rsconcept/frontend/src/components/select/ToolbarGraphSelection.tsx similarity index 93% rename from rsconcept/frontend/src/components/select/GraphSelectionToolbar.tsx rename to rsconcept/frontend/src/components/select/ToolbarGraphSelection.tsx index ec06db95..3a074964 100644 --- a/rsconcept/frontend/src/components/select/GraphSelectionToolbar.tsx +++ b/rsconcept/frontend/src/components/select/ToolbarGraphSelection.tsx @@ -14,21 +14,21 @@ import { import { CProps } from '../props'; import MiniButton from '../ui/MiniButton'; -interface GraphSelectionToolbarProps extends CProps.Styling { +interface ToolbarGraphSelectionProps extends CProps.Styling { graph: Graph; core: number[]; setSelected: React.Dispatch>; emptySelection?: boolean; } -function GraphSelectionToolbar({ +function ToolbarGraphSelection({ className, graph, core, setSelected, emptySelection, ...restProps -}: GraphSelectionToolbarProps) { +}: ToolbarGraphSelectionProps) { return (
{!isLoading && !error && !hasNoData ? ( - + {children} ) : null} {!isLoading && !error && hasNoData ? ( - + Данные не загружены ) : null} diff --git a/rsconcept/frontend/src/context/OptionsContext.tsx b/rsconcept/frontend/src/context/ConceptOptionsContext.tsx similarity index 100% rename from rsconcept/frontend/src/context/OptionsContext.tsx rename to rsconcept/frontend/src/context/ConceptOptionsContext.tsx diff --git a/rsconcept/frontend/src/context/LibraryContext.tsx b/rsconcept/frontend/src/context/LibraryContext.tsx index 16e2c11d..3561ecd0 100644 --- a/rsconcept/frontend/src/context/LibraryContext.tsx +++ b/rsconcept/frontend/src/context/LibraryContext.tsx @@ -24,7 +24,7 @@ import { RSFormLoader } from '@/models/RSFormLoader'; import { contextOutsideScope } from '@/utils/labels'; import { useAuth } from './AuthContext'; -import { useConceptOptions } from './OptionsContext'; +import { useConceptOptions } from './ConceptOptionsContext'; interface ILibraryContext { items: ILibraryItem[]; @@ -65,12 +65,12 @@ interface LibraryStateProps { } export const LibraryState = ({ children }: LibraryStateProps) => { - const { user } = useAuth(); + const { user, loading: userLoading } = useAuth(); const { adminMode } = useConceptOptions(); const [items, setItems] = useState([]); const [templates, setTemplates] = useState([]); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [processing, setProcessing] = useState(false); const [loadingError, setLoadingError] = useState(undefined); const [processingError, setProcessingError] = useState(undefined); @@ -92,8 +92,8 @@ export const LibraryState = ({ children }: LibraryStateProps) => { if (!filter.folderMode && filter.head) { result = result.filter(item => item.location.startsWith(filter.head!)); } - if (filter.folderMode && filter.folder) { - result = result.filter(item => item.location == filter.folder); + if (filter.folderMode && filter.location) { + result = result.filter(item => item.location == filter.location); } if (filter.type) { result = result.filter(item => item.item_type === filter.type); @@ -175,16 +175,18 @@ export const LibraryState = ({ children }: LibraryStateProps) => { const reloadTemplates = useCallback(() => { setTemplates([]); getTemplates({ - setLoading: setLoading, - onError: setLoadingError, + setLoading: setProcessing, + onError: setProcessingError, showError: true, onSuccess: newData => setTemplates(newData) }); }, []); useEffect(() => { - reloadItems(); - }, [reloadItems]); + if (!userLoading) { + reloadItems(); + } + }, [reloadItems, userLoading]); useEffect(() => { reloadTemplates(); diff --git a/rsconcept/frontend/src/context/UserProfileContext.tsx b/rsconcept/frontend/src/context/UserProfileContext.tsx index 7185a553..7c39352e 100644 --- a/rsconcept/frontend/src/context/UserProfileContext.tsx +++ b/rsconcept/frontend/src/context/UserProfileContext.tsx @@ -36,7 +36,7 @@ interface UserProfileStateProps { export const UserProfileState = ({ children }: UserProfileStateProps) => { const { users } = useUsers(); const [user, setUser] = useState(undefined); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [processing, setProcessing] = useState(false); const [error, setError] = useState(undefined); const [errorProcessing, setErrorProcessing] = useState(undefined); diff --git a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx index f6768572..82ef4bf3 100644 --- a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx @@ -1,13 +1,15 @@ 'use client'; import clsx from 'clsx'; -import { useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; +import SelectLocationContext from '@/components/select/SelectLocationContext'; import SelectLocationHead from '@/components/select/SelectLocationHead'; import Label from '@/components/ui/Label'; import Modal, { ModalProps } from '@/components/ui/Modal'; import TextArea from '@/components/ui/TextArea'; import { useAuth } from '@/context/AuthContext'; +import { useLibrary } from '@/context/LibraryContext'; import { LocationHead } from '@/models/library'; import { combineLocation, validateLocation } from '@/models/libraryAPI'; import { limits } from '@/utils/constants'; @@ -22,9 +24,16 @@ function DlgChangeLocation({ hideWindow, initial, onChangeLocation }: DlgChangeL const [head, setHead] = useState(initial.substring(0, 2) as LocationHead); const [body, setBody] = useState(initial.substring(3)); + const { folders } = useLibrary(); + const location = useMemo(() => combineLocation(head, body), [head, body]); const isValid = useMemo(() => initial !== location && validateLocation(location), [initial, location]); + const handleSelectLocation = useCallback((newValue: string) => { + setHead(newValue.substring(0, 2) as LocationHead); + setBody(newValue.length > 3 ? newValue.substring(3) : ''); + }, []); + function handleSubmit() { onChangeLocation(location); } @@ -41,13 +50,19 @@ function DlgChangeLocation({ hideWindow, initial, onChangeLocation }: DlgChangeL className={clsx('w-[35rem]', 'pb-3 px-6 flex gap-3')} >
-
+